AWK:在一个字段中打印具有最大值的所有行 每个字段,包括具有最大值的相同行和多列

AWK: print ALL rows with MAX value in one field Per the other field including Identical Rows with Max value AND multiple columns

提问人:SWK 提问时间:2/10/2023 最后编辑:SWK 更新时间:2/11/2023 访问量:127

问:

我很感激我被许多贡献者迅速提供的许多解决方案所感动!!(AWK:在一个字段中打印所有具有最大值的行 每个字段,包括具有最大值的相同行)

这个问题包括多一列的数据,我想将每列 2 中具有最高值的行保留在每列 1 中,包括包含多列的数据中具有最大值的相同行,并打印所有列。

数据

a   130 data1
a   55  data2
a   66  data3
b   88  data4
b   99  data5
b   99  data6
c   110 data7
c   130 data8
c   130 data9

所需输出

a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

@jared_mamrot 中的代码可以完美运行并打印出所有列。

awk 'NR==FNR{if($2 > max[$1]){max[$1]=$2}; next} max[$1] == $2' file file

Wildberg 提供的代码@Andre也可以完美运行并打印出所有列

awk 'arr[$1] < $2{arr[$1] = $2}
       arr[$1] == $2{n[$1,arr[$1]]++; line[$1,arr[$1],n[$1,arr[$1]]] = $0}
       END{for(i in arr){
             j=0; do{j++; print line[i,arr[i],j]} while(j < n[i,arr[i]])}}' file

@Ed Morton 下面的 awk 脚本也非常适合我以前的 2 列数据。它打印两列;键和值。

我的另一个问题是,当我的数据中有多个列时,我应该如何修改此脚本以打印所有列。

sort file | awk '
    { cnt[$1,$2]++; max[$1]=$2 }
    END { for (key in max) { val=max[key]; for (i=1; i<=cnt[key,val]; i++) print key, val } }
' 

谢谢大家的大力帮助!!

数组 awk

评论


答:

2赞 markp-fuso 2/10/2023 #1

假设一行可能有 3 个以上的字段:

$ cat file
a   130 data1
a   55  data2
a   66  data3
b   88  data4
b   99  data5
b   99  data6
c   110 data7
c   130 data8
c   130 data9 data10 data11

修改当前代码的一个想法:awk

awk '
    { key=$1; val=$2                    # save 1st two fields
      $1=$2=""                          # clear 1st two fields
      gsub(/^[[:space:]]+/,"")          # remove leading white space from line

      ++cnt[key,val]
      max[key]=(val > max[key] ? val : max[key])
      row[key,val,cnt[key,val]]=$0      # save rest of line
    }
END { for (key in max) { 
          val=max[key]
          for (i=1; i<=cnt[key,val]; i++) 
              print key, val, row[key,val,i]
      }
    }
'  file

这将生成:

a 66 data3
b 99 data5
b 99 data6
c 130 data8
c 130 data9 data10 data11

评论

1赞 SWK 2/10/2023
我正要要求@anubhava超过 3 个字段。也感谢您的额外解释!!
3赞 anubhava 2/10/2023 #2

您只需要一个额外的关联数组来存储第 3 列作为值,将键存储为前 2 列,并将运行计数器存储在变量中:cnt

awk '{
   map[$1,$2,++cnt[$1,$2]] = $0
   max[$1] = ($2 > max[$1] ? $2 : max[$1])
}
END {
   for (key in max) {
      val = max[key]
      for (i=1; i<=cnt[key,val]; i++)
         print map[key,val,i]
   }
}' file

a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

无需对此解决方案的文件进行排序。awk

评论

3赞 SWK 2/10/2023
我终于可以完全理解 max[$1]=($2 > max[$1] ? $2 : max[$1]) 跳过排序。谢谢!!!
1赞 anubhava 2/10/2023
@SWK:我进行了更新,以处理输入中任意数量的字段,而不仅仅是 3 个字段。
1赞 dawg 2/10/2023 #3

同样的 ruby 在稍作调整后即可工作:

ruby -e '
grps=$<.read.split(/\R/).
    group_by{|line| line[/^\S+/]}
# {"a"=>["a   130 data1", "a   55  data2", "a   66  data3"], "b"=>["b   88  data4", "b   99  data5", "b   99  data6"], "c"=>["c   110 data7", "c   130 data8", "c   130 data9"]}

maxes=grps.map{|k,v| v.max_by{|s| s.split[1].to_f}}.map{|s| s.split[0..1] }
# [["a", "130"], ["b", "99"], ["c", "130"]]

grps.values.flatten.each{|s| puts s if maxes.include?(s.split[0..1])}
' file 

指纹:

a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

一旦你开始进入 3 个或更多列进行管理,使用 ruby(或 Perl、Python 等)会更容易,因为它支持切片、分组和连接数组。

4赞 Ed Morton 2/10/2023 #4

使用任何 awk 和排序:

$ sort -k1,1 -k2,2nr file | awk '!seen[$1]++{max=$2} $2==max'
a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

或:

$ sort -k1,1 -k2,2nr file | awk '$1!=prev{prev=$1; max=$2} $2==max'
a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

在意识到我想多了之前的原始脚本:

$ sort -k1,1 -k2,2nr file | awk '!seen[$1]++{key=$1; max=$2} $1==key && $2==max'
a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9

当任何给定值 $1 首次出现在输入中时,值为 0,当相同的 $1 再次出现时,该值为某个递增的非零数字。因此,的值是(即 在条件上下文中),第一次在输入中看到给定值,然后()出现。因此,第一次出现时,我们设置了 2 美元的任何值,即 在这种情况下。这就是 .seen[$1]++!seen[$1]++1true$0falsea$1keyamax130!seen["a"]++

从那时起,我们只打印 $1 和 $2 的每一行,在本例中,这只是输入的第一行。a130

然后,当第一次看到 1 美元时,也会发生同样的情况。b

评论

0赞 SWK 2/10/2023
哇!我不会生成新问题^^,但是虽然语法只包括 $1 和 $2,但“!seen[$1]++”如何不排除第 3 行或第 5 行?以及“seen[$1]++”如何在第一次看到 $1 后保持静止?再次感谢!
1赞 Ed Morton 2/11/2023
@SWK我添加了解释,如果您有任何其他问题,请告诉我。
1赞 SWK 2/11/2023
非常感谢您的彻底!!现在我看到了 seen[$1]++ 和 !seen[$1]++ 的实际函数,并且 {key=$1; max=$2} 确实每 $1 设置 $2(实际上是双向的)。因此,即使 $2==max 也意味着 $1==key && $2==max 在这里。我真的很感激我至少节省了几个月的时间来学习这么多,感谢你们所有的贡献者!
1赞 Ed Morton 2/11/2023
你提到的@SWK让我意识到,如果我使用,我根本不需要保存或测试,而且我什至不需要那个 $1 值数组,只是前一个读的。我更新了脚本。even $2==max implies $1==key && $2==max herekeyseen[]
2赞 ufopilot 2/10/2023 #5
awk '
    $1 != firstcol{ firstcol=$1; max=$2; map[NR]=$0 } 
    $1 == firstcol{
        if($2>max){ map[NR--]=$0; max=$2 }
        if($2==max) map[NR]=$0
    }
    END{
        for(i in map) print map[i]
    }
' inputfile

a   130 data1
b   99  data5
b   99  data6
c   130 data8
c   130 data9