【问题标题】:awk group by multiple columns and print max value with non-primary keyawk 按多列分组并使用非主键打印最大值
【发布时间】:2016-09-28 04:08:11
【问题描述】:

我是这个网站的新手,正在尝试学习 awk。我试图找到field3的最大值,按field1分组并打印所有具有最大值的字段。字段 2 包含时间,这意味着对于每个 item1,字段 2、字段 3 和字段 4 有 96 个值

输入文件:(逗号分隔)

item1,00:15,10,30
item2,00:45,20,45
item2,12:15,30,45
item1,00:30,20,56
item3,23:00,40,44
item1,12:45,50,55
item3,11:15,30,45

想要的输出:

item1,12:45,50,55
item2,12:15,30,45
item3,11:15,30,45

到目前为止我尝试了什么:

BEGIN{
FS=OFS=","}
{
if (a[$1]<$3){
   a[$1]=$3}
}
END{
for (i in a ){
print i,a[i]
}

但这只会打印

item1,50
item2,30
item3,30

但我需要打印相应的 field2 和 field4 以及所需的输出中显示的最大值。请帮忙。

【问题讨论】:

  • 注意输出应该是item3,23:00,40,44,因为40是item3最大的第三列。
  • 如果可能发生,则在您的输入/输出中包含具有相同 $1 和 $3 但不同 $2 值的行,以便我们可以查看您是否希望输出第一条或最后一条或所有匹配的行。如果不能发生,请说明。
  • @Ed Morton 相同的 1 美元,2 美元不同,3 美元可以相同。关于您提出的解决方案,我只是不明白不使用 !($1 in max) 块的后果。我想到的另一件事是,如果按两列分组,说 $1 和 $2 那么这个 !($1 in max) 块和排序是如何工作的?
  • @fox 1) for same $1, $2 is different, $3 can be same 然后按照我的建议将该案例包含在您的示例输入/输出中,以便我们可以看到您希望如何处理它(有几个选项)。 2) consequence of not using the !($1 in max) block - 如果你有输入行 item4,11:15,0,45 没有它,你会得到一个空行输出而不是那个输入行(想想 max[$1] 在你填充它之前有什么值)。 3) 你会使用max[$1,$2] 和!(($1,$2) in max)。
  • 我最近遇到了这个空行问题,但不知道为什么。感谢您的精彩解释。现在我知道了原因,学到了一件好事。

标签: awk gawk


【解决方案1】:

这里的问题是你没有存储整行,所以当你浏览最终数据时没有完整的数据要打印。

你需要做的是使用另一个数组,比如data[index]=full line:

BEGIN{
FS=OFS=","}
{
 if (a[$1]<$3){
   a[$1]=$3
   data[$1]=$0}       # store it here!
}
END {
   for (i in a )
       print data[i]  # print it here
}

或者作为单行:

$ awk 'BEGIN{FS=OFS=","} {if (a[$1]<$3) {a[$1]=$3; data[$1]=$0}} END{for (i in a) print data[i]}' file
item1,12:45,50,55
item2,12:15,30,45
item3,23:00,40,44

【讨论】:

    【解决方案2】:

    借助sort 命令的一点帮助:

    sort -t, -k1,1 -k3,3nr file | awk -F, '!seen[$1]++'
    

    【讨论】:

    • @EdMorton 谢谢! :) 让我补充一点。我最近告诉过 GNU sort -u 扩展的帮助有限。这个案例就是一个很好的例子。虽然基本上有-u 标志,但在大多数情况下,您需要更详细的功能来删除重复数据。虽然很多 GNU 扩展对 imo 很有帮助,至少对于交互使用而言,sort -u 可能不值得扩展。
    • -u 不是 GNU 扩展,它从第一天就存在于所有 sorts。与sort | uniq 相比,它实际上非常有用。如果您可以将它与一个键相关联,那就太好了,这样您就可以执行sort -t, -k3,3nr -k1,1u file 之类的操作并跳过管道到 awk!
    • 该死!我认为是这样,因为一方面是 imo 做得太多,另一方面又做得太少。虽然我实际上有时希望sort 能像你建议的那样工作,但sort -t, -k3,3nr | uniq -k1,1u 会不会更像unix,因为每个工具都做得很好?实际上我更怀念uniq 中的功能,然后是sort。
    • 我的想法是,如果您可以对键进行数字排序和/或反向排序,那么为什么不唯一呢?无论如何,我没有看到它发生和nbd。也许这是最好的,idk。这是我使用这些工具 35 年来第一次想到它,所以我显然并没有太想念它!
    【解决方案3】:

    要稳健地完成这项工作,您需要:

    $ cat tst.awk
    BEGIN { FS="," }
    !($1 in max) {
        max[$1]  = $3
        data[$1] = $0
        keys[++numKeys] = $1
    }
    $3 > max[$1] {
        max[$1]  = $3
        data[$1] = $0
    }
    END {
        for (keyNr=1; keyNr<=numKeys; keyNr++) {
            print data[keys[keyNr]]
        }
    }
    
    $ awk -f tst.awk file
    item1,12:45,50,55
    item2,12:15,30,45
    item3,23:00,40,44
    

    在进行最小/最大值计算时,您应该始终使用读取的第一个值作为最小/最大值的种子,而不是假设它总是小于或大于某个任意值(例如,如果您跳过!($1 in max) 上面的块)。

    您需要keys 数组在打印输出时保留输入顺序。如果你改用in,那么输出顺序将是随机的。

    请注意,惯用的 awk 语法很简单:

    <condition> { <action> }
    

    不是 C 风格:

    { if ( <condition> ) { <action> } }
    

    【讨论】:

      猜你喜欢
      • 2022-11-16
      • 2013-05-23
      • 2018-09-05
      • 1970-01-01
      • 2020-06-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多