ある行の列が別の行の列と一致する場合は、次を追加します。

ある行の列が別の行の列と一致する場合は、次を追加します。

同じ表の最初の列に与えられた項目に基づいて、下の表の列にリストされている数値を合計したいと思います。表の内容は次のとおりです。

10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F

予想される結果は次のとおりです(2番目と最後の列にグループ化されたデータ)。

12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F

Linuxでawkを使用してこの出力を取得するにはどうすればよいですか?

答え1

$ cat tst.sh
#!/usr/bin/env bash

awk '
    BEGIN { FS=OFS="," }
    $2 != vals[2] {
        if ( NR>1 ) {
            prt()
        }
        split($0,vals)
        next
    }
    {
        for ( i=1; i<=NF; i++ ) {
            if ( $i+0 == $i ) {
                vals[i] += $i
            }
        }
    }
    END {
        prt()
    }
    function prt(    i) {
        for (i=1; i<=NF; i++) {
            printf "%s%s", vals[i], (i<NF ? OFS : ORS)
        }
    }
' "${@:--}"

$ ./tst.sh file
12,Mumbai,1,7,7,0,10,7,59,M
4,Chennai,5,6,7,8,9,0,6,F

入力ファイルがまだ2番目のフィールドにグループ化されていない場合(公開された入力例のように)、以下を変更します。

awk '...' "${@:--}"

これに関して:

sort -t',' -k2,2 "${@:--}" | awk '...'

答え2

2番目の列をキーとして使用し、出力中にレコードの順序を維持しながらこれを実行できます。

awk -F, -v OFS=, '!seen[$2]++{ recNr++ }
{ for(i=1; i<=NF; i++)
      if(i!=2 && i!=NF)
          sumCol[recNr, i, $2]+= $i
      else 
          sumCol[recNr, i, $2]= $i (i==NF?ORS:"")
}

END{ for (key in sumCol){
         if(sumCol[key]!=""){
             recNumbr++; sep=""
             split(key, tmp, SUBSEP)
             for(j=1; j<=NF; j++){
                 printf ("%s", sep sumCol[recNumbr, j, tmp[3]])
                 sep=OFS
                 delete sumCol[recNumbr, j, tmp[3]]
             }
         }
     }
}' infile

答え3

GNUの使用datamash:

$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
4,Chennai,5,6,7,8,9,0,6,F
12,Mumbai,1,7,7,0,10,7,59,M

これはdatamash、列1と列3〜9を合計し、入力を列2と10の組み合わせにグループ化します。

datamashグループ化された列は出力から最初に出力されるため、2番目のステップを実行して元の順序datamashに並べ替えます。

出力はグループ化列に基づいてソートされるため、以前に入力ChennaiされますMumbai。ソースデータがすでにソートされている場合は、-sコマンドから削除します。

他の例:

$ cat file
10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M
$ datamash -s -t , groupby 2,10 sum 1,3-9 <file | datamash -t , cut 3,1,4-10,2
8,Chennai,10,12,14,16,18,0,12,F
4,Chennai,5,6,7,8,9,0,6,M
12,Mumbai,1,7,7,0,10,7,59,M

答え4

使用幸せ(以前のPerl_6)

~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join("\t"), as => {$_.split(",").[ 0,2..*-2 ][*;*]});  \
            for %class.kv -> $k,$v {say $k => $v.elems > 1 ?? [Z+] $v<> !! $v[*;*]};'   file

OPは、この問題を解決するためにPerl言語シリーズを使用することを検討できます。上記は、Rakuを使用する1つの方法のみを示しています。つまり、2番目と最後の列(コンマの後のインデックス)をlines読み、編集します。この列情報は分類子にも保持されている場合は重複するため、このパラメータはコンポーネントから数字ではなく2つの列を削除するために使用されます。データはハッシュに保存されます。classify[1, *-1]splitkeyvalueasclassifyvalue%class

ここでは、ペアをキー/値コンポーネント%classに分割して印刷し、Rakuの三項演算子を使用してテストして、複数の要素が含まれていることを確認します。複数の要素が見つかると、列が合計されて使用されます(コンテナ化されていない次の要素ごとに合計されます)。要素が1つしかない場合、列は削除されます(合計されず、平面化のみ)。kvkeyvalueelemsput[Z+] $v<>put$v[*;*]

入力例:

10,Mumbai,0,4,5,0,6,3,55,M
2,Mumbai,1,3,2,0,4,4,4,M
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,F
4,Chennai,5,6,7,8,9,0,6,M

出力例(タブ区切りkeys):

Chennai M => (4 5 6 7 8 9 0 6)
Chennai F => (8 10 12 14 16 18 0 12)
Mumbai  M => (12 1 7 7 0 10 7 59)

Rakuでは、カンマで区切られた出力は確かに可能ですが、以下の答えでは、単純化のために2つの「グループ化」列を列1と列2に抽象化します。

~$ raku -e 'my %class = lines.classify(*.split(",").[1, *-1].join(","), as => {$_.split(",").[ 0,2..*-2 ][*;*]});  \
            for %class.kv -> $k,$v {put $k ~","~ ($v.elems > 1 ?? [Z+] $v<> !! $v[*;*]).join(",")};'  file
Chennai,F,8,10,12,14,16,18,0,12
Mumbai,M,12,1,7,7,0,10,7,59
Chennai,M,4,5,6,7,8,9,0,6

最後に、より広いサンプル入力データセットを提供した@Kusalanandaに感謝します。

https://docs.raku.org/routine/classify
https://docs.raku.org/言語/operators#index-entry-operator_ternary
https://raku.org

関連情報