web-dev-qa-db-ja.com

awkでの数値のフォーマットと丸めの問題

値の列の平均を見つけようとしたときに、正確な数値を取得したいと考えています。

たとえば、これは入力値の列です。

1426044
1425486
1439480
1423677
1383676
1360088
1390745
1435123
1422970
1394461
1325896
1251248
1206005
1217057
1168298
1153022
1199310
1250162
1247917
1206836

次のコマンドを使用すると:

... | awk '{ sum+=$1} END { print sum/NR}'

次の出力が表示されます:1.31638e+06。ただし、1316375.05以上の正確な数値が、この形式で必要です1,316,375.05

コマンドラインツールのみでこれを行うにはどうすればよいですか?

EDIT 1

最大、最小、平均を取得する次の1行のawkコマンドを見つけました。

awk 'NR == 1 { max=$1; min=$1; sum=0 } { if ($1>max) max=$1; if ($1<min) min=$1; sum+=$1;} END {printf "Min: %d\tMax: %d\tAverage: %.2f\n", min, max, sum/NR}'

NRを1に初期化する必要があるのはなぜですか? NR == 1を削除すると、間違った結果が表示されます。

編集2

1つのコマンドで数値のリストの最小値、最大値、中央値、平均値を取得する方法はありますか? から次のawkスクリプトを見つけました。数値データの単一列の合計、カウント、平均、中央値、最大値、および最小値をすべて一度に取得します。 stdinから読み取り、出力のタブ区切りの列を1行に出力します。少し微調整しました。上記のawkコマンド(最初の編集)とは異なり、NR == 1は必要ないことに気付きました。誰かが理由を説明できますか?数値データが並べ替えられて配列に配置されていることに関係していると思います。

#!/bin/sh

sort -n | awk '

  $1 ~ /^(\-)?[0-9]*(\.[0-9]*)?$/ {
    a[c++] = $1;
    sum += $1;
  }
  END {
    ave = sum / c;
    if( (c % 2) == 1 ) {
      median = a[ int(c/2) ];
    } else {
      median = ( a[c/2] + a[c/2-1] ) / 2;
    }

    {printf "Sum: %d\tCount: %d\tAverage: %.2f\tMedian: %d\tMin: %d\tMax: %d\n", sum, c, ave, median, a[0], a[c-1]}
  }
'
4
rplee
... | awk '{ sum+=$1} END { print sum/NR}'

デフォルトでは、(GNU)awkは最大6桁の有効数字(および指数部)を出力します。これは the OFMT variable のデフォルト値から来ています。それはドキュメントでそれを言っていませんが、これは非整数値の数値にのみ適用されます。

OFMTを変更してすべてのprintステートメントに影響を与えるか、ここでprintfを使用するだけでよいので、平均が偶数の場合でも機能します。何かのようなもの %.3fは、小数点以下3桁の数字を出力します。

...| awk '{ sum+=$1} END { printf "%.3f\n", sum/NR }'

fgの意味、および精度修飾子(.prec 2番目のリンク):

awk 'NR == 1 { max=$1; min=$1; sum=0 } ...'

これはNRを初期化しません。代わりに、NRが1に等しいかどうか、つまり最初の行にいるかどうかをチェックします。 (==は比較、=は代入です。)その場合、maxminおよびsumを初期化します。それがないと、maxminはゼロから始まります。負の最大値や正の最小値を持つことはできません。

7
ilkkachu

GNU awkを使用している場合は、これを試してください。'修飾子を使用してコンマを追加します。

$ awk '{sum+=$1}END{printf "%'\''.2f\n",sum/NR}' filename
1,316,375.05
$

jqがある場合は、これを試してください。

$ jq -s min,max,add/length filename
1153022
1439480
1316375.05
$

From gnu.org:gawk Format Modifiers から

単一引用符またはアポストロフィ文字は、ISO CのPOSIX拡張です。これは、浮動小数点値の整数部分、または整数10進値の整数部分に、桁区切り記号が含まれている必要があることを示します。これは、そのような文字をサポートするロケールでのみ機能します。例えば:

6
steve

値の列の平均を見つけようとしたときに、正確な数値を取得したいと考えています。

13の数字の短いリストであっても、「正確な数字」に対する答えはありません。

$ echo "scale=500; 1 / 13 " | bc
.0769230769230769230769230769230769230769230769230769230769230769230\
76923076923076923076923076923076923076923076923076923076923076923076\
92307692307692307692307692307692307692307692307692307692307692307692\
30769230769230769230769230769230769230769230769230769230769230769230\
76923076923076923076923076923076923076923076923076923076923076923076\
92307692307692307692307692307692307692307692307692307692307692307692\
30769230769230769230769230769230769230769230769230769230769230769230\
7692307692307692307692307

これはinfinite floatであり、終了することはないため、「正確な答え」を返す方法はありません。しかし、平均が何であるかの合理的な答えがあなたが望むものであるなら、あなたはprintfで遊んで始めることができます:

$ awk '{sum+=$1} END { printf "%.10f\n",sum/NR }' file
1316375.0500000000

これは、20の数値を追加する場合、小数点以下2桁で十分であり、これで十分なことを意味します。

$ awk '{sum+=$1} END { printf "%.2f\n",sum/NR }' file
1316375.05

(GNU awkで)1000の区切り文字を取得するには、次のように使用できます。

$ awk '{sum+=$1} END { printf "%'\''.10f\n",sum/NR }' file
1,316,375.05
0
Isaac