如何使用datamash对所有列进行操作?

如何使用datamash对所有列进行操作?

假设我有以下数据文件:

111 222 333
444 555 666
777 888 999

我可以使用 GNU Datamash 计算每列的总和,如下所示:

cat foo | datamash -t\  sum 1 sum 2 sum 3
1332 1665 1998

如果我不知道数据文件中的列数,我将如何使用 datamash 执行此操作?

我问这个问题是因为例如cut支持范围结束符号,例如-其字段选择器。

答案1

我没有看到指定未知范围的选项数据混合手册

试试这个perl单线

$ perl -lane '$s[$_]+=$F[$_] for 0..$#F; END{print join " ", @s}' ip.txt
1332 1665 1998
  • -a选项将自动在空格上分割输入行,结果保存在@F数组中
  • for 0..$#F循环数组,$#F给出最后一个元素的索引
  • $s[$_]+=$F[$_]将总和保存在@s数组中,默认情况下初始值将0在数字上下文中。$_每次迭代都会有索引值
  • END{print join " ", @s}处理完所有输入行后,@s以空格作为分隔符打印数组内容

答案2

cols=$( awk '{print NF; exit}' foo); cat foo | datamash -t\  sum 1-$cols

或者

cat foo | datamash -t\  sum 1-$( awk '{print NF; exit}' foo)

datamash有指定列范围的功能,因此计算列数并将该结果用作范围规范的一部分。在我的示例解决方案中,我过去awk只检查文件的第一行并退出,但您可以使用适合您喜欢的任何其他内容。datamash本身有一个-check函数,其输出包括列数,但其格式仍需要解析您感兴趣的特定数字。

答案3

我不知道datamash,但这里有一个awk解决方案:

$ awk '{ for( col=1; col<=NF; col++ ) { totals[col]+=$col } } END { for( col=0; col<length(totals); col++ ) {printf "%s ", totals[col]}; printf "\n" } ' input
1332 1665 1998

为了使该awk脚本更具可读性:

{      // execute on all records
  for( col=1; col<=NF; col++ ) { 
    totals[col]+=$col 
  }; 
} 
END {  // execute after all records processed
  for( col=0; col<length(totals); col++ ) {
    printf "%s ", totals[col]
  }; 
  printf "\n";
} 

答案4

使用datamashbash

n=($(datamash -W check < foo)); datamash -W sum 1-${n[2]} < foo

输出:

1332    1665    1998

怎么运行的:

  1. datamash -W check < foo输出字符串“3行,3个字段”

  2. n=($(datamash -W check < foo))将该字符串加载到数组中$n。我们想要字段的数量,即${n[2]}

  3. datamash -W sum 1-${n[2]} < foo剩下的就完成了。


这也可以通过POSIXshell,使用复杂的printf格式化字符串而不是数组,但它更粗糙:

datamash -W sum 1-$(printf '%0.0s%0.0s%s%0.0s' $(datamash -W check < foo)) < foo

也可以使用 shell 工具来完成:

datamash -W sum 1-$(head -1 foo | wc -w) < foo

相关内容