根据匹配字段对列总和


11

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

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列对文件进行排序,并使用来对最后几列求和,但仅适用于单独的行,而不适用于第2 匹配的两行。awksortawk



@glennjackman:在每个文件中,列1的值相同。它用作文件的标识符(我有45个标识符),并将用于某些下游过程。对于我的问题,它也可能会被忽略(或删除),然后再次添加。
TomPio

或者,以$1 $2键为准。
glenn jackman

Answers:


12

我会在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

7

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

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

glenn jackman在有关排序的评论中提到的:

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

2
如果您有GNU awk,请使用PROCINFO["sorted_in"] = "@ind_num_asc"代替管道sort。裁判gnu.org/software/gawk/manual/html_node/...
格伦·杰克曼

@taliezin:感谢taliezin和terdon。两种方法都具有魅力。非常感谢您的帮助。
TomPio

1
@taliezin:正如我说的那样,这两种方法都对我有用,我将terdon答案标记为“正确”的答案。我想这就是您的意图。再次感谢。
TomPio

1
如果我理解您想要总唯一键的问题,我们可以添加一个计数器并将其打印出来:awk'{map [$ 2] + = $ 3 + $ 4; } END {表示(地图中的i){打印“ 2”,i,地图[i] | “排序-t'n'”; cnt ++; }打印“总唯一性:” cnt}'文件
taliezin

1
几乎是一样的:awk'{map [$ 2] + = $ 3 + $ 4; oc [$ 2] ++; } END {代表(i在地图中){打印“ 2”,i,map [i],oc [i] | “排序-t'n'”; }}',现在您将看到另一列带有实例的列。
taliezin 2015年

4

您可以对数据进行预排序,然后让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}何时$2与当前行(p!=$2)的触发时间不同。


请注意,即使第一列具有不同的值,您也可以使用sort -k2该值对第二列进行排序
-gaoithe

2

使用瑞士军刀工具mlr

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'产生一个新的第5个字段,即字段34的总和。

  • stats1函数(或“ 动词 ”)是一个较小的瑞士军刀
    的较大瑞士军刀内mlr,与若干基于累加器的功能,例如sumcountmean等。

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.