Unix 有几个实用程序用于在流上执行类似关系代数的操作(grep
、join
、cut
、 的附加内容awk
)。是否有现成可用的分组聚合实用程序(或可在大多数 Linux 发行版上安装)?
目标是获取一个文件,其中一列中有一些键,另一列中有一些值,例如:
foo.txt u1 394082
bar.txt u2 3948
frob.c u1 29322
并输出一个文件,该文件具有一列的唯一值以及另一列中的某些值的聚合。例如,第三列与第二列之和:
$ aggregate --sum=3 --group-by=2 <data
u1 423404
u2 3948
这样的实用程序是否存在(Perl、Awk 等,单行不算),还是有待编写?
答案1
我想我在另一个 StackOverflow 问题中找到了这个答案,但我发现“q”对于这个目的非常有用:https://github.com/harelba/q。
例如,您的示例目标可以这样实现:
$ q "select c2, sum(c3) from data group by c2"
u1 423404
u2 3948
由于它使用 sqlite 作为后端,因此您可以使用各种 sqlite 函数进行计算。
答案2
有一些限制GNU Recutils可以做到这一点。首先,它需要是 CSV 文件,而不是 TSV(Recutils 似乎不喜欢 TSV 文件),并且需要一个标头。但我可以这样做:
csv2rec foo.csv |recsel -G user -p 'user,sum(size)' |rec2csv
不确定这比 Perl 或 Awk 单行代码是好是坏。
答案3
如果 awk 的单行代码不算数,也许您会喜欢以下 shell (bash/ksh) 单行代码:
sort -k2 data | ( while read c1 c2 c3; do if [ "$prev" = "$c2" ]; then
sum=$(expr $c3 + $sum); else if [ $prev ]; then echo $prev $sum; fi;
sum=$c3; prev=$c2; fi; done; echo $prev $sum)
除了sort
and expr
(分别用于分组和求和)之外,这里使用的有趣成分是 while 内的 read 语句。括号创建了本地化$prev
和$sum
变量所必需的子流程。