【问题标题】:Grouping common elements using awk使用 awk 对常见元素进行分组
【发布时间】:2017-05-03 18:48:19
【问题描述】:

下表说明了我希望操作的数据的简要快照。我正在寻找一个 awk 脚本,它将相似的元素分组到一个组中。例如。如果你看下表:

  1. 数字 (1,2,3,4,6) 应该都属于一个组。所以 row1 row2 row4 row8 将是组“1”
  2. 数字 9 是唯一的,没有任何共同的元素。所以它会单独驻留在一个单独的组中,比如第 2 组
  3. 类似的数字 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

Numberlist 即 $3 是我分组的唯一考虑因素。

【问题讨论】:

    标签: linux shell unix awk grouping


    【解决方案1】:

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

    [[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 具有关联数组,每个元素都由一个字符串键访问。早期的方法是将每个组存储在一个数组中,其中键是组的索引。因此,当我们读取列时,我们将读取每个组,将组拆分为单个元素,将每个元素与列中的每个元素进行比较。但是,如果我们将元素存储在一个数组中,而不是存储一个组,其中键是元素本身,键的值是元素所属组的索引。因此,当我们读取一列时,我们将列拆分为单个元素 (split($3,a,",");),然后检查数组中的元素是否存在以 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;。所以这里所有的访问都是线性的,因为我们使用元素直接访问一个键。因此,不会为每个元素一次又一次地分解一个组,因此时间更短。我的第一种方法是基于我自己的一个问题(我在第一行中提到过),它是更复杂的处理但更短的数据集。但这需要一个更简单直接的逻辑。

    【讨论】:

    • 谢谢,阿巴苏。该解决方案就像一个魅力。这只是将要处理的数据的一个小快照。实际数据将接近2000万条。我希望它能承受压力。我会做测试并告诉你。同时,您可以记录您的代码。我发现内部循环有点难以理解。
    • 已更新,如果有效,请将其标记为已接受,如果没有,请告诉我们。如果大数据集正在运行,请在找到匹配项后使用break 以减少运行时间
    • 当然,我会的。再次感谢。
    • 2100 万条测试数据在 7 分钟内得到处理!!巨大的成就。
    • 4,5,6 也应该属于 group1 而不是 group2。这是我的错。我的测试文件使用的是 $3 列,而我的主文件使用的是 $2。这就是混乱。我把它纠正了。它现在工作正常。我认为这个解决方案很好。感谢您的努力。如果你能解释一下步骤,那就太好了,尽管我已经开始努力理解逻辑了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-25
    • 1970-01-01
    • 1970-01-01
    • 2011-03-12
    • 2020-11-16
    • 1970-01-01
    相关资源
    最近更新 更多