【问题标题】:BASH - conditional sum of columns and rows in csv fileBASH - csv 文件中列和行的条件总和
【发布时间】:2015-06-15 15:24:33
【问题描述】:

我有一个带有一些数据库基准测试结果的 CSV 文件,示例如下:

Date;dbms;type;description;W;D;S;results;time;id
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;570;265;50
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;420;215;50
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;500;365;50
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;530;255;50

Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;870;265;99
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;620;215;99
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;700;365;99
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;530;255;99

我需要处理具有相同id(最后一列的值)的所有行并得到:

Date;dbms;type;description;W;D;S;time;results;results/time
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;sum column 8;sum column 9;(sum column 8 / sum column 9)
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;sum column 8;sum column 9;(sum column 8 / sum column 9)

现在我只能用 awk 命令对第 8 列求和:

awk -F";" '{print;sum+=$8 }END{print "sum " sum}' ./file.CSV

编辑:
需要对 iam 已经使用的脚本进行一些修改的帮助。这是真实的输入数据:

Date;dbms;type;description;W;D;time;TotalTransactions;NOTransactions;id
Mon Jun 15 14:53:41 CEST 2015;sqlite;in-memory;TPC-C test results;2;1;10;272270;117508;50
Mon Jun 15 15:03:46 CEST 2015;sqlite;in-memory;TPC-C test results;2;1;10;280080;110063;50
Mon Jun 15 15:13:53 CEST 2015;sqlite;in-memory;TPC-C test results;5;1;10;144170;31815;60
Mon Jun 15 15:13:53 CEST 2015;sqlite;in-memory;TPC-C test results;5;1;10;137570;33910;60
Mon Jun 15 15:24:04 CEST 2015;hsql;in-memory;TPC-C test results;2;1;10;226660;97734;70
Mon Jun 15 15:34:08 CEST 2015;hsql;in-memory;TPC-C test results;2;1;10;210420;95113;70
Mon Jun 15 15:44:16 CEST 2015;hsql;in-memory;TPC-C test results;5;1;10;288360;119328;80
Mon Jun 15 15:44:16 CEST 2015;hsql;in-memory;TPC-C test results;5;1;10;270360;124328;80

我需要对时间、TotalTransactions 和 NOTransactions 列的值求和,然后添加一个具有值的列(sum NOTransactions/sum time)

我正在使用这个脚本:

awk 'BEGIN {FS=OFS=";"}
(NR==1) {$10="results/time"; print $0} 
(NR>1 && NF) {sum7[$10]+=$7; sum8[$10]+=$8; sum9[$10]+=$9;  other[$10]=$0} 
 END {for (i in sum8) 
        {$0=other[i]; $7=sum7[i];$8=sum8[i]; $9=sum9[i]; $10=sprintf("%.0f", sum9[i]/sum7[i]); print}}' ./logsFinal.csv

给我这个输出:

;;;;;;;;;results/time
Mon Jun 15 15:03:46 CEST 2015;sqlite;in-memory;TPC-C test results;2;1;20;552350;227571;11379
Mon Jun 15 15:13:53 CEST 2015;sqlite;in-memory;TPC-C test results;5;1;20;281740;65725;3286
Mon Jun 15 15:34:08 CEST 2015;hsql;in-memory;TPC-C test results;2;1;20;437080;192847;9642
Mon Jun 15 15:44:16 CEST 2015;hsql;in-memory;TPC-C test results;5;1;20;558720;243656;12183
Date;dbms;type;description;W;D;0;0;0;-nan

值看起来不错(标题行除外)。但我需要得到这些结果没有 id 列(我想删除 id 列) 所以我需要获得相同的值,但不是在 id 列中识别具有相同值的已处理行,它必须是在 dbms AND W AND D 列中具有相同值的行

【问题讨论】:

  • 提示:awk -F';' '{col8[$NF]+=$8; col9[$NF]+=$9} END {for (i in col8) print i, col8[i]}' file。此外,最好打印出 exact 预期的输出,以便清楚我们都理解相同的内容。

标签: linux bash csv awk


【解决方案1】:

你可以使用这个 awk:

awk 'BEGIN{ FS=OFS=";" }
NR>1 && NF {
        s=""
        for(i=1; i<=7; i++)
           s=s $i OFS;
        a[$NF]=s;
        sum8[$NF]+=$8
        sum9[$NF]+=$9
} END{
   for (i in a)
       print a[i] sum8[i], sum9[i], (sum9[i]?sum8[i]/sum9[i]:"NaN")
}' file
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;2020;1100;1.83636
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;2720;1100;2.47273

【讨论】:

  • 谢谢。我不使用, 作为字段分隔符是愚蠢的。但是我没有注意到还需要平均值。
【解决方案2】:

此 awk 程序将打印修改后的标题并修改输出以包含总和及其除法:

awk 'BEGIN {FS=OFS=";"} 
     (NR==1) {$10="results/time"; print $0} 
     (NR>1 && NF) {sum8[$10]+=$8; sum9[$10]+=$9; other[$10]=$0} 
     END {for (i in sum8) 
            {$0=other[i]; $8=sum8[i]; $9=sum9[i]; $10=(sum9[i]?sum8[i]/sum9[i]:"NaN"); print}}'

给出:

Date;dbms;type;description;W;D;S;results;time;results/time
Mon Jun 15 14:22:20 CEST 2015;sqlite;on-disk;text;2;1;1;2020;1100;1.83636
Mon Jun 15 14:22:20 CEST 2015;hsql;on-disk;text;2;1;1;2720;1100;2.47273

您似乎并不关心结果中的 ID,但如果您这样做,只需将 $10= 替换为 $11=

另外,如果您需要根据多列的值求和,您可以创建一个临时变量(在下面的示例中为a),它是两列的串联并将其用作数组,像这样:

awk 'BEGIN {FS=OFS=";"}
     (NR==1) {$10="results/time"; print $0}
     (NR>1 && NF) {a=$5$6; sum8[a]+=$8; sum9[a]+=$9; other[a]=$0}
     END {for (i in sum8)
            {$0=other[i]; $8=sum8[i]; $9=sum9[i]; $10=(sum9[i]?sum8[i]/sum9[i]:"NaN"); print}}'

【讨论】:

  • 您的 awk 代码对我来说似乎工作正常。我不明白你提到的标题的问题。
  • 我得到的是:Date;dbms;type;description;W;D;time;TotalTransactions;NOTransactions;results/time' 'Mon Jun 15 15:03:46 CEST 2015;sqlite;in-memory;TPC-C test results;2;1;20;552350;227571;11379
  • 查看我的编辑,然后请清理您的问题。
  • 它几乎是我需要的,但总和是基于 W 和 D 列我还需要列 dbms。我已将您的脚本更改为 sum column time 并尝试根据 dbms 值添加 make sum:awk 'BEGIN {FS=OFS=";"} (NR==1) {$10="results/time"; print $0} (NR&gt;1 &amp;&amp; NF) {a=$2; b=$5$6; c=$b$a; sum7[c]+=$7; sum8[c]+=$8; sum9[c]+=$9; other[c]=$0} END {for (i in sum8) {$0=other[i]; $7=sum7[i]; $8=sum8[i]; $9=sum9[i]; $10=(sum9[i]?sum8[i]/sum9[i]:"NaN"); print}}' ./input.csv 但这给了我不好的输出
猜你喜欢
  • 1970-01-01
  • 2016-09-07
  • 2014-06-10
  • 2015-03-19
  • 1970-01-01
  • 2013-01-07
  • 1970-01-01
  • 2018-05-14
  • 2021-03-24
相关资源
最近更新 更多