根据匹配字段对列进行求和

根据匹配字段对列进行求和

我有一个以下格式的大文件:

2 1019 0 12 
2 1019 3 0 
2 1021 0 2 
2 1021 2 0 
2 1022 4 5
2 1030 0 1 
2 1030 5 0 
2 1031 4 4

如果值在第2栏匹配,我想对其中的值求和第3栏4两行的值,否则只是唯一行中值的总和。

所以我希望的输出如下所示:

2 1019 15 
2 1021 4 
2 1022 9 
2 1030 6 
2 1031 8

我能够根据以下条件对文件进行排序第2栏使用awksort并对最后一列求和awk,但仅适用于单行,不适用于两行,其中第2栏火柴。

答案1

我会在 Perl 中这样做:

$ perl -lane '$k{"$F[0] $F[1]"}+=$F[2]+$F[3]; 
              END{print "$_ $k{$_}" for keys(%k) }' file 
2 1019 15
2 1021 4
2 1030 6
2 1031 8
2 1022 9

或者 awk:

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file 

如果您希望根据第二列对输出进行排序,您可以通过管道传输到sort

awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file | sort -k2

请注意,两个解决方案都包含第一列。这个想法是使用第一列和第二列作为哈希(在 perl 中)或关联数组(在 awk 中)的键。每个解决方案的关键是column1 column2,如果两行具有相同的第二列但不同的第一列,它们将被单独分组:

$ cat file
2 1019 2 3
2 1019 4 1
3 1019 2 2

$ awk '{a[$1" "$2]+=$3+$4}END{for (i in a){print i,a[i]}}' file
3 1019 4
2 1019 10

答案2

也许这会有所帮助,但是第 1 列是否始终为 2,结果是否取决于它?

awk '{ map[$2] += $3 + $4; } END { for (i in map) { print "2", i, map[i] | "sort -t't'" } }' file

或如所提到的格伦·杰克曼关于排序的评论:

gawk '{ map[$2] += $3 + $4; } END { PROCINFO["sorted_in"] = "@ind_str_asc"; for (i in map) { print 2, i, map[i] } }' file

答案3

您可以对数据进行预排序并让 awk 处理详细信息:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s} {s+=$3+$4} {p=$2}'

您可能需要重置累加器:

sort -n infile | awk 'NR>1 && p!=$2 {print p,s;s=0} {s+=$3+$4} {p=$2}'

输出:

1019 15
1021 19
1022 28
1030 34

如果您确实想要保留第一列,请执行以下操作:

sort -n infile | awk 'NR>1 && p!=$1FS$2 {print p,s} {s+=$3+$4} {p=$1FS$2}'

输出:

2 1019 15
2 1021 19
2 1022 28
2 1030 34

解释

p变量保存$2前一行的值,或者$1FS$2在上面的第二种情况下。这意味着当前一行与当前行不同时{print p,s}触发( )。$2p!=$2

答案4

使用瑞士军刀utilmlr:

mlr --nidx   put '$5=$3+$4'   then   stats1 -g 1,2 -f 5 -a sum   infile

输出:

2   1019    15
2   1021    4
2   1022    9
2   1030    6
2   1031    8

笔记:

  • --nidx告诉mlr使用数字字段名称。

  • put '$5=$3+$4'创造一个新的第五名字段、字段之和34

  • 函数stats1(或“动词
    ") 是较大瑞士军刀中较小的瑞士军刀mlr,具有多种基于累加器的功能,例如sum, count, mean,ETC。

    stats1 -g 1,2按列对数据进行分组12-f 5 -a sum然后将这些组的字段相加5stats1 仅打印命名字段。

相关内容