AWKを使用したブロックの合計(モード変更時の合計の再開)

AWKを使用したブロックの合計(モード変更時の合計の再開)

次のファイルがあります。

A 100
A 200
A 300 #sum=600
B 400
B 500 #sum=900
A 600
A 700
A 800 #sum=2100

私は出力が次のようになります:

A 600
B 900
A 2100
C sum_of_C
D sum_of_D

forsedおよびを使用してgrepこれを行うことができますawk

ところで勉強中だから台本awkを書きたいですawk。これまで私は以下を持っています:

if (${NR {print $1}} == ${NR-1 {print $1}}) 
  sum+=$2
  print $0"\t"sum
else
  sum=$2
  print $0"\t"sum

awk -f awkscript file成功しませんでした。解決策は何ですか?

答え1

ifあなたがそこで何をしたいのかわかりません。目標の場合、フィールド数NRに使用されるレコード数。これらの真ん中にブロックをNF配置することはできません。{}

あなたの目標は、この行のフィールド値を前の行のフィールド値と比較し、新しいデータ「グループ」に達したときに合計を印刷することです。その場合、このスクリプトはあなたが望むことを行い、あなたの目標とほぼ同じだと思います。

{
    if (last && $1 != last) {
        print last, sum
        sum = 0
    }
    sum = sum + $2
    last = $1
}
END {
    print last, sum
}

last前の行の最初のフィールド()値を保持する新しい変数を作成します。$1私たちはこれを見て、私たちが見ているグループを追跡するために使用します。

  • { ... }各行に対して(最上位レベルにあるため)最初にa)がlast設定されているか(最初の行に何も印刷しないため)、b)最初のフィールドの値がlast。そうであればlast、値、空白、,計算sumされた内容を印刷します。 (タブをご希望の場合は"\t"先ほど引用符で囲んで使用してください)
  • 印刷後、sumゼロにリセットされます。
  • $2どちらにしても、2番目のフィールド()の値をに追加しますsum
  • 各行に最初のフィールド(グループ)を保存することで、last次の行の比較に使用できます。
  • 最後に、最後のグループを印刷しようとしています。この目的のために、我々はEND { ... }ブロックを使用します。データが不足すると、プログラムの終了時に実行されます。以前のように、合計で作業しているグループを印刷します。

私が実行した場合:

awk -f sum.awk < data

あなたのデータファイルを使用して、次のような結果を得る。

A 600
B 900
A 2100

予想通り。


awkでも別の方法でも、これを行うより簡単な方法があります。特に、上記の本文を次のように置き換えることができます。

last && $1 != last {
    print last, sum
    sum = 0
}
{
    sum = sum + $2
    last = $1
}

ここでは、明示的なテストの代わりにawkの条件付きブロック構文を使用しますif。プログラムは上記と同じように動作しますが、より慣用的です。この例には大きな違いはありませんが、awkを学んでいるかどうかを知っておくと便利です。


#sum=提供されたファイルの例が実際に行(または同様のもの)を持つファイルの例である場合は、次のスクリプトを使用できます。

{
    sum = sum + $2
    if (NF == 3) {
        print $1, sum
        sum = 0
    }
}

各行に対して、2番目のフィールドの値をsum変数に追加します。正確に3つのフィールド(NF == 3)を含む行から合計を印刷し、sumゼロにリセットします。

答え2

ファイルがすべての合計をメモリに格納できるほど小さい場合は、次のように簡単な操作を実行できます。

$ awk '{sum[$1]+=$2}END{for(pat in sum){print pat,sum[pat]}}' file 
A 2700
B 900

これはコメントスクリプトと同じですawk

#!/usr/bin/awk -f

{
    ## Here, we use $1 as the key of an associative array
    ## and increment its current value by $2. The result of 
    ## this will be an array element for each different $1 in 
    ## the file whose value will be the sum of all associated $2s.
    sum[$1]+=$2
}

## The END{} block is exacuted after the entire file
## has been processed.
END{
    ## Iterate through the keys of the array (the $1s),
    ## saving each as 'pat'. Then, print the current value of
    ## 'pat' as well as the associated value (the sum) from
    ## the array.
    for(pat in sum){
        print pat,sum[pat]
    }
}

このアプローチで発生する可能性のある唯一の問題は、行が多すぎるとs配列を維持すると$1メモリが不足することです。現代のシステムでは、このようなことが起こる可能性はほとんどありません。一方、この方法はソートされていないファイルを処理できるため、ファイルの行が順番にソートされていない場合でも機能します。

関連情報