浮動小数点データを含む複数の列の合計を計算するシナリオがあります。
以下のコードは整数値には機能しますが、フローティングペイント値には機能しません。
コードはこのデータに完全に適合します。
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
最初はシェルでこれをしないでください。しかし、awk
.シェルでは浮動小数点演算を実行する方法がわかりません。
あなたのスクリプトは、解析するファイル名と考慮するフィールド名に対応する一連のパラメータを使用しているようです。
awk
各列に対して2回呼び出すよりも効率的にこれを行うことができます。
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
同じ列を2回リストすると、2つの合計が発生します。
$ awk -v cols="SAL|SAL" -f script.awk file1
SAL|SAL
155|155
2番目のファイルでは:
$ 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
CSVkitなどのCSV認識ツールを使用して操作を実行することもできます。
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