下表说明了我希望处理的数据的简要快照。我正在寻找一个awk脚本,它将相似的元素分为一组。例如。如果您查看下表:


数字(1,2,3,4,6)应该全部属于一个组。因此,row1 row2 row4 row8将成为组“ 1”
数字9是唯一的,没有任何共同的元素。因此它将单独存在于另一个组中,例如第2组
类似地,数字5,7将驻留在一组中,例如组3,依此类推...


文件:

heading1        heading2         numberlist     group
name1           text             1,2,3          1
name2           text             2              1
name3           text             9              2
name4           text             1,4            1
name5           text             5,7            3
name6           text             7              3
name7           text             8              4
name8           text             6,2            1


我正在搜索类似于我的查询,并找到了此链接。 Grouping lists by common elements。但是解决方案是使用C ++而不是AWK,这是我的主要要求。

顺便说一句,我还发现了这个awk解决方案,该解决方案与我的查询有些相关,但是它没有处理逗号分隔的值。
awk script grouping with array

号码表,即$ 3是我分组的唯一考虑。

最佳答案

这个问题似乎与我的问题之一几乎相同,我在您的示例中使用了一个专栏来解决我的问题:)所以...

[[bash_prompt$]]$ cat log ; echo "########"; \
> cat test.sh ;echo "########";  awk -f test.sh log
heading1        heading2         numberlist     group
name1           text             1,2,3
name2           text             2
name3           text             9
name4           text             1,4
name5           text             5,7
name6           text             7
name7           text             8
name8           text             6,2
########
/^name/{
  i=0; j=0;
  split($3,a,",");
  for(var in a) {
    for(var1 in q) {
      split(q[var1],r,",");
      for(var2 in r) {
        if(r[var2] == a[var]) {
          i=1;
          j=((var1+1));
        }
      }
    }
  }
    if(i == 0) {
      q[length(q)] = $3;
      j=length(q);
    }
  print $1 "\t\t" $2 "   \t\t" $3 "\t\t" j;
}
########
name1           text            1,2,3           1
name2           text            2               1
name3           text            9               2
name4           text            1,4             1
name5           text            5,7             3
name6           text            7               3
name7           text            8               4
name8           text            6,2             1
[[bash_prompt$]]$


更新:

split用传入第三个参数的定界符分割第一个参数,并将其放入第二个参数指向的数组中。这里的主数组是q,它保存一个组的组成员,它基本上是一个数组数组,其中元素的索引是组ID,而元素是集合组中所有成员的元素。因此,q[0]="1,2,3"表示第0个组包含成员123。现在在awk中,读取的第一行以name/^name/)开头。然后将第三个字段(1,2,3)分解为数组a。现在,对于数组a中的每个元素,我们将每个组存储到q(for(var1 in q))中,然后在每个组内部,将它们拆分为另一个临时数组r(split(q[var1],r,",")),即“ 1,2,3”被拆分为数组r。现在,将r中的每个元素与a中的元素进行比较。如果找到匹配项,则组的索引就是该行的索引(数组索引从0开始,组的索引从1开始,因此使用((var1 + 1))。如果找不到,只需将其添加为q和最后一个索引+1,即数组的长度是该行的索引

更新:

/^name/{
  j=0;
  split($3,a,",");
  for(var in a) {
    if(q[a[var]] != 0) {
      j=q[a[var]]; i=1;
      break;
    }
  }
  j = (j == 0) ? ++k : j;
  for(var1 in a) {
    if(q[a[var1]] == 0) {
      q[a[var1]] = j;
    }
  }
  print $1 "\t\t" $2 "   \t\t" $3 "\t\t" j;
}


更新:

base是awk,具有关联数组,并且每个元素都可以通过字符串键访问。较早的方法是将每个组存储在一个数组中,其中key是组的索引。因此,当我们阅读一列时,我们将阅读每个组,将组拆分为单个元素,然后将每个元素与列中的每个元素进行比较。但是,如果我们将元素存储在数组中,而不是存储组,则其中key是元素本身,而key的值是元素所属组的索引。因此,当我们读取一列时,我们将该列拆分为单个元素(split($3,a,",");),然后检查数组中的element是否存在组索引,并且该元素作为if(q[a[var]] != 0)(awk中的键),如果元素不存在,默认情况下,将在其中初始化值为0的元素,因此检查q[a[var]] != 0。如果找到任何元素,我们将元素的组索引作为列的索引并中断。否则j保持为0。如果j保持为0,则++k给出最新的组索引。现在,我们找到了列元素的组索引。需要将该索引带到不属于任何其他组的那些元素中(在某些情况下,同一列中的多个元素属于不同的组,这里我们采用先到先得的方法,但不要过度使用已经属于另一个组的其他组的组索引)。因此,对于列(for(var1 in a))中的每个元素,如果它不属于组(if(q[a[var1]] == 0)),则为其指定组索引q[a[var1]] = j;。所以这里所有访问都是线性的,因为我们直接使用元素作为键来访问。因此,不会为每个元素一次又一次地分解一组,因此时间更短。我的第一种方法是基于我自己的一个问题(我在第一行中提到过),该问题处理起来更复杂,但数据集更短。但这需要一种更简单直接的逻辑。

07-24 15:53