web-dev-qa-db-ja.com

AWKで計算して合計で除算する

次のdataファイルがあるとします...

foo     10
bar     20
oof     50
rab     20

...列2の合計のパーセントとして列2を印刷するにはどうすればよいですか?つまり、欲しい...

foo     10    10%
bar     20    20%
oof     50    50%
rab     20    20%

...もちろん、それほど明白ではない数字を使用します。積算合計を簡単に作成できますが、どうすればよいかわかりません行を出力する前に合計を計算します。私はこれをawkファイルで行っていますtotals.awk...

#!/usr/bin/awk -f
BEGIN{
        runningtotal=0
}
{
        runningtotal=runningtotal+$2
        print $1 "\t" $2 "\t" runningtotal "\t" $2/runningtotal
}

したがって、./totals.awk data利回り...

foo     10      10      1
bar     20      30      0.666667
oof     50      80      0.625
rab     20      100     0.2

2回ループする方法、1回は合計を計算する方法、1回は行を印刷する方法はありますか?これはAWKで可能ですか、それとも他のユーティリティを使用する必要がありますか?

7
Rip Leeb

awkを1回呼び出すだけでテーブルを作成するには:

$ awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' data data
foo     10      10%
bar     20      20%
oof     50      50%
rab     20      20%

使い方

ファイルdataは、awkへの引数として2回提供されます。その結果、変数sに格納されている合計を取得するために1回目、出力を印刷するために2回目に2回読み取られます。コマンドをさらに詳しく見てみましょう:

  • FNR==NR{s+=$2;next;}

    NRはawkが読み取ったレコード(行)の総数であり、FNRは現在のファイルからこれまでに読み取られたレコードの数です。したがって、FNR==NRの場合、最初のファイルを読み取っています。これが発生すると、変数sは2番目の列の値によって増分されます。次に、nextawkに残りのコマンドをスキップして次のレコードからやり直すように指示します。

    sをゼロに初期化する必要はないことに注意してください。 awkでは、すべての数値変数はデフォルトでゼロに初期化されます。

  • printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s

    このコマンドに到達すると、2番目のファイルを処理しています。これは、sが列2の合計を保持することを意味します。したがって、列1、列2、およびパーセンテージ100*$2/sを出力します。

出力形式オプション

printfを使用すると、出力形式を詳細に制御できます。上記のコマンドは、文字列、整数、および浮動小数点に対して機能する%s形式指定子を使用しています。ここで役立つ他の3つのオプションは次のとおりです。

  • %dは、数値を整数としてフォーマットします。数値が実際には浮動小数点の場合、整数に切り捨てられます

  • %fは、数値を浮動小数点としてフォーマットします。幅と小数点以下の桁数を%5.2fなどのように指定することもできます。

  • %eは指数表記を提供します。これは、いくつかの数値が非常に大きいまたは小さい場合に役立ちます。

シェル関数を作る

これを複数回使用する場合は、長いコマンドを入力するのは不便です。代わりに、コマンドを開く関数またはスクリプトを作成します。

totalsという関数を作成するには、次のコマンドを実行します。

$ totals() { awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' "$1" "$1"; }

この関数を定義すると、dataというデータファイルのパーセンテージは次のコマンドを実行して確認できます。

$ totals data

totalsの定義を永続的にするには、~/.bashrcファイルに配置します。

シェルスクリプトを作成する

スクリプトが必要な場合は、totals.shという名前のファイルを作成し、次の内容を含めます。

#!/bin/sh
awk 'FNR==NR{s+=$2;next;} {printf "%s\t%s\t%s%%\n",$1,$2,100*$2/s}' "$1" "$1"

dataというデータファイルのパーセンテージを取得するには、次のコマンドを実行します。

sh totals.sh data
13
John1024

1つのファイルを開いた状態での完全な方法

awk '{a[NR]=$0;x+=(b[NR]=$2)}END{while(++i<=NR)print a[i]"\t"100*b[i]/x"%"}' file

foo     10      10%
bar     20      20%
oof     50      50%
rab     20      20%

これは他よりも多くのメモリを使用しますが、より高速になるはずです

これにより、行が配列aに読み込まれ、フィールド2が配列bに読み込まれます。
次に、フィールド2の値だけxをインクリメントします。

最後に、1からレコード数まで反復し、正しい行を出力してパーセンテージを計算します。

4
user78605

これを行う「単純な」方法は、awkを2回呼び出すことです。1回で合計を取得し、もう1回で比率を計算します。

$ total=$(awk 'BEGIN{ total=0 } { total=total+$2 } END{ printf total }' data)
$ awk -v total=$total '{ print $1 "\t" $2 "\t" 100*$2/total "%" }' data

誰かがワンライナーをどうにかするだろうと確信しています...

3
John WH Smith