提问人:SWK 提问时间:2/10/2023 最后编辑:SWK 更新时间:2/11/2023 访问量:127
AWK:在一个字段中打印具有最大值的所有行 每个字段,包括具有最大值的相同行和多列
AWK: print ALL rows with MAX value in one field Per the other field including Identical Rows with Max value AND multiple columns
问:
我很感激我被许多贡献者迅速提供的许多解决方案所感动!!(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 } }
'
谢谢大家的大力帮助!!
答:
假设一行可能有 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
评论
您只需要一个额外的关联数组来存储第 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
评论
同样的 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 等)会更容易,因为它支持切片、分组和连接数组。
使用任何 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]++
1
true
$
0
false
a
$1
key
a
max
130
!seen["a"]++
从那时起,我们只打印 $1 和 $2 的每一行,在本例中,这只是输入的第一行。a
130
然后,当第一次看到 1 美元时,也会发生同样的情况。b
评论
even $2==max implies $1==key && $2==max here
key
seen[]
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
评论