我有一个场景,我想计算包含浮点数据的多个列的总和。
我的下面的代码适用于整数值,但不适用于浮动油漆值。
代码非常适合此数据
ID|NAME|SAL|COST|PER|TAG
1|A|10|10|20|10|
1|B|10|15|20|10|
1|C|10|17|25|80|
1|D|115|110|20|100|
1|E|10|10|10|10|
代码 :
#!/bin/bash
FILE="$1"
COLUMNS="${@:2}"
for col in $COLUMNS; do
colnum=$(awk -v RS='|' '/'$col'/{ print NR; exit}' $FILE)
awk '{FS="|"}{s+='$colnum'}END{print "'$col' ", s}' $FILE
done | column -t
如何使用上面的代码获得以下数据的总和?
ID|NAME|SAL|COST|PER|TAG
1|A|9.234|123.12|20.123|67.1|
1|B|10.12|153.234|20.90|190.34|
1|C|8.234|17.01|25.777|80.09|
1|D|11.2|11.222|10.1|10.00000|
1|E|16.23|10.1|145.22|11.77278|
新代码不起作用:
#!/bin/bash
FILE="$1"
COLUMNS="${@:2}"
for col in $COLUMNS; do
awk -F'|' '{T+=$col} END { printf "%.10f\n", T }' $FILE
done | column -t
答案1
首先不要在 shell 中执行此操作,而是在awk
. shell 不知道如何进行浮点运算。
您的脚本似乎正在获取一组与要解析的文件名和要考虑的字段名称相对应的参数。
awk
与为每列调用两次相比,您可以更有效地执行此操作。
BEGIN { OFS = FS = "|" }
FNR == 1 {
# Handle the header row. Assuming "cols" is a FS-separated
# string of column names that we want to sum, pick out the
# column numbers that we want to process and put them in the
# array "col". This also converts the "cols" string into a
# corresponding "header" array.
nf = split(cols, header)
for (i = 1; i <= NF; ++i)
for (j = 1; j <= nf; ++j)
if ($i == header[j])
col[j] = i
next
}
{
# Sum each column that we have enumerated in the "col" array.
for (i = 1; i <= nf; ++i)
sum[i] += $(col[i])
}
END {
# Output a two row table containing only the headers that we
# have summed over and the accumulated sums.
$0 = ""
for (i = 1; i <= nf; ++i)
$i = header[i]
print
for (i = 1; i <= nf; ++i)
$i = sum[i]
print
}
使用以下命令在您的第一个文件上运行此命令:
$ awk -v cols="SAL|COST|PER|TAG" -f script.awk file1
SAL|COST|PER|TAG
155|162|95|210
将同一列列出两次会得出两次总和:
$ awk -v cols="SAL|SAL" -f script.awk file1
SAL|SAL
155|155
在第二个文件上:
$ awk -v cols="SAL|COST|PER|TAG" -f script.awk file2
SAL|COST|PER|TAG
55.018|314.686|222.12|359.303
使用后处理column -t
:
$ awk -v cols="SAL|COST|PER|TAG" -f script.awk file2 | column -s '|' -t
SAL COST PER TAG
55.018 314.686 222.12 359.303
将其放入一个简单的脚本中:
#!/bin/sh
infile=$1
shift
IFS='|'
awk -v cols="$*" -f script.awk "$infile" |
column -s '|' -t
你可以用它作为
$ ./script.sh file1 PER TAG
PER TAG
95 210
您还可以使用支持 CSV 的工具(例如 CSVkit 中的工具)来为您完成这项工作。
csvstat
能够计算 CSV 文件的一些基本“统计数据”。假设您的文件是格式正确的 CSV 文件(它们不是,因为每个数据行都有一个尾随|
分隔符),则每列的总和可以通过
$ csvstat --sum file1
1. ID: None
2. NAME: None
3. SAL: 155
4. COST: 162
5. PER: 95
6. TAG: 210
$ csvstat --sum file2
1. ID: None
2. NAME: None
3. SAL: 55.018
4. COST: 314.686
5. PER: 222.12
6. TAG: 359.30278
或者,仅针对单个列:
$ csvstat --sum -c 'SAL' file2
55.018
答案2
我的解决方案将发挥作用
对于此数据集 1 - 对此进行测试
ID|NAME|SAL|COST|PER|TAG
1"|"A"|"50.1123"|"10.1"|"25.22"|"10.2"|"
2"|"B"|"50.11"|"15.45"|"25.1"|"10.1118"|"
对于此数据集 2 - 对此进行测试
ID|NAME|SAL|COST|PER|TAG
1|A|9.234|123.12|20.123|67.1
2|B|10.12|153.234|20.90|190.34
3|C|8.234|17.01|25.777|80.09
4|D|11.2|11.222|10.1|10.00000
5|E|16.23|10.1|145.22|11.77278
有效的解决方案
FILE_NAME="$1"
COLUMN_NAME="$2"
alpha=( $(awk -F"|" 'NR==1{for(i=1;i<=NF;i++){if ($i ~ /'$COLUMN_NAME'/){print i;}}}' $FILE_NAME) )
for each in "${alpha[@]}"
do
#echo "$each"
awk -F'"?\\|"?' '{T+=$('$each')} END { printf "%.4f\n", T }' $FILE_NAME
done
像这样跑
bash script.sh DEMO.txt 'SAL|COST|PER|TAG'
输出1:
100.2223
25.5500
50.3200
20.3118
输出2:
55.0180
314.6860
222.1200
359.3028