2列と3列に基づいてコンマ区切りのテキストファイルをマージして要約し、4列の平均を計算します。

2列と3列に基づいてコンマ区切りのテキストファイルをマージして要約し、4列の平均を計算します。

すべてのイベントとサイズを報告するファイルがあります。上の行の列3が、下の行の列2と同じであると識別できる連続イベントをマージして、その情報を要約しようとしています(列1が同じ場合)。同じ形式で連続した文字列が複数行の場合、列 2 はその列のグループ化の最下位の数字、列 3 はその列のグループ化の最上位の数字、列は単一の行に置き換えられます。 4は、列4のすべての数値の平均(最も近い整数に丸められます)になります。

明確に言えば、最初の列は特定のグループ、2番目の列は開始位置、3番目の列は終了位置、4番目の列は発生回数です。

列の組み合わせは各行ごとに一意であり、列は列1、列2、列3に基づいて事前にソートされています。列 2 の数字は同じであってはいけません/同じであってはいけません。最初の列は同じでもよく、通常同じです。

可能であれば、awkでこれを達成しようとしていますが、試してみましたが失敗しました。私が試したことは次のとおりです。

awk 'BEGIN {OFS=","} NR==1 {print} NR>1 {if ($1==prev && $2==end+1) {sum+=$4; count++; end=$3} else {if (NR>2) {print prev, start, end, int(sum/count+0.5);}; prev=$1; start=$2; end=$3; sum=$4; count=1}} END {print prev, start, end, int(sum/count+0.5)}'
###### reformatted via "awk -o- '...script_body...'"

awk '
BEGIN {
        OFS = ","
}

NR == 1 {
        print
}

NR > 1 {
        if ($1 == prev && $2 == end + 1) {
                sum += $4
                count++
                end = $3
        } else {
                if (NR > 2) {
                        print prev, start, end, int(sum / count + 0.5)
                }
                prev = $1
                start = $2
                end = $3
                sum = $4
                count = 1
        }
}

END {
        print prev, start, end, int(sum / count + 0.5)
}'

入力例:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44

出力例:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

答え1

列 1 は同じですが、3 番目の列と次の行の 2 番目の列との間に間隔があるいくつかの入力行を追加します。

$ cat raw.dat
fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44            # 3rd column (current line) does not match ...
fgh2,63543524,63543525,20            # 2nd column (next line)
fgh2,63543525,63543526,60

メモ:OPの説明とサンプルデータでこれが起こることができるかどうかは不明です。提案されたコードは引き続き機能する必要があり、削除される可能性があり|| $2 != outcols[3]ます。

一般的なアプローチ:

  • 配列内の次の出力列セットを追跡します。
  • 条件が一致しない場合は、配列を印刷して配列を消去し、次の出力行で配列の塗りつぶしを開始します。

アイデアawk

awk '
BEGIN { FS = OFS = "," }

function print_line () {

    if (outcols[1]) {                             # if we have something in outcols[1] then ...
       for (i=1;i<=3;i++)                         # loop through 1st three columns and ...
           printf "%s%s", outcols[i], OFS         # print to stdout then ...
       printf "%.0f\n", (sum / count)             # calculate/print average; let printf/.0f do the rounding
    }

    delete outcols                                # clear array
    sum = count = 0                               # reset counters
}

($1 != outcols[1]) ||
($2 != outcols[3])    { print_line() }

                      { if (! outcols[1]) {       # if nothing in outcols[1] then initialize 1st two output columns ...
                           outcols[1] = $1
                           outcols[2] = $2
                        }
                        outcols[3] = $3
                        sum += $4
                        count++
                      }

END                   { print_line() }            # flush last line to stdout

' raw.dat

これで以下が生成されます。

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

答え2

頑張ってくれた皆さんに感謝します。いくつかは非常に近づき、私の答えを再び考え、整理するのに役立ちました。

入力しようとすると、次の解決策が機能しているようです。

awk 'BEGIN {
    FS = ","
    OFS = ","
}

{
    if (NR == 1) {
        group = $1
        start = $2
        end = $3
        sum = $4
        count = 1
    } else if ($1 == group && $2 == end) {
        end = $3
        sum += $4
        count++
    } else {
        printf("%s,%d,%d,%d\n", group, start, end, int((sum + count / 2) / count))
        group = $1
        start = $2
        end = $3
        sum = $4
        count = 1
    }
}

END {
    printf("%s,%d,%d,%d\n", group, start, end, int((sum + count / 2) / count))
}'

元の質問を入力:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44

元の質問の出力:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

2を入力してください:

fgh1,45513382,45513383,43
fgh1,45513383,45513384,44
fgh1,45513384,45513385,44
fgh1,45513385,45513386,43
fgh1,45513386,45513387,43
fgh1,45513387,45513388,44
fgh2,63543512,63543513,44
fgh2,63543513,63543514,41
fgh2,63543514,63543515,44
fgh2,63543515,63543516,44
fgh2,63543524,63543525,20
fgh2,63543525,63543526,60

出力2:

fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

答え3

$1awkを使用して、一度に1つの値のみをメモリに保存します。

$ cat tst.awk
BEGIN { FS=OFS="," }
($1 != prev[1]) || ($2 != prev[3])  {
    prt()
    beg = $2
    cnt = sum = 0
}
{
    end  = $3
    sum += $4
    cnt ++
    split($0,prev)
}
END { prt() }
function prt() {
    if ( cnt ) {
        print prev[1], beg, end, int( (sum / cnt) + 0.5 )
    }
}

$ awk -f tst.awk orig_input
fgh1,45513382,45513388,44
fgh2,63543512,63543516,43

$ awk -f tst.awk input2
fgh1,45513382,45513388,44
fgh2,63543512,63543516,43
fgh2,63543524,63543526,40

上記は.5s を四捨五入すると仮定します。

答え4

テストされていませんが、私の解決策は次のとおりです。

{
   AGG=$2 "," $3;
   if ( AGG == PREVAGG ) { 
      TOT+=$4;
      COUNT+=1;
   } else {
      if (PREVAGG) {
         print PREVAGG "," int(TOT/COUNT);
      }
      TOT=$4;
      COUNT=1;
      PREVAGG=AGG;
   }
}
END { print PREVAGG "," int(TOT/COUNT); }

可能であれば、これをawkでソートしようとします。

入力ストリームのレコード順序を参照していますか?もしそうなら、awkはそれほど上手ではありません。努力するsort -k 1,2,3 -t','

コードはファイルのヘッダー行を処理しているようですが、期待した出力にはありません。私はこれを見落としました。

どのように動作しますか?

AGG=$2 "," $3- $ 2と$ 3が変更されたら、累積データを出力してリセットします。これを複合エンティティとして扱うと、一部のコードが節約され、複雑さが軽減されます。また、他の列を考慮するのも簡単です(S1の値で何をすべきかを指定していません)。

if ( AGG == PREVAGG ) {- この入力行に集計されたラベルは、前の入力行のラベルと同じですか?

  • その場合は、引き続きデータを集計してください。
  • それ以外の場合は、アキュムレータと最後の行ラベルをリセットして集計データを出力します。ただし、これが最初の入力レコードでない場合にのみ該当します。

スクリプトが最後のレコードに達した後、データ出力をトリガーするために$ 2、$ 3に変更はありません。したがって、これはENDブロックに明示的に設定されます。

関連情報