ヘッダーを一致させて列を1つのファイルにマージする

ヘッダーを一致させて列を1つのファイルにマージする

複数の行と列を持つファイルを取得し、一致するヘッダーを持つ列の値を水平方向に追加(合計)したいと思います。私のファイル(この例ではありません)は、最初の9列と最初の行を無視します。結果に影響を与えずに印刷したいからです。

列は繰り返される順序ではなく、例よりも多くの列があります。

アイデアは次のとおりです。

入力する:

var x y x y x y
a 1 0 1 1 0 1
b 1 1 0 0 1 1
c 1 1 0 0 0 0

出力:

var x y
a 2 2
b 2 2
c 1 1

私がこれまでに得たものは次のとおりです...

awk -F '\t' '{FS==OFS} FNR==1; FNR>1 {for (i=10; i<=NF; i++) {} print}' FILE.tsv > FILE_norepcols.tsv

また、可能であれば、このコードをよりよく書く方法を理解しようとしています。

答え1

$ perl -lane '$,="\t";
   print(qw/var x y/),next if $. == 1;
   push @A, shift @F;
   $A[$|--+1] += $_ for @F;
   print splice @A;
' file

結果:

var x   y
a   2   2
b   2   2
c   1   1

仮定:

  • フィールド数が奇数です。
  • フィールド#2とそれ以降のフィールドは両方とも数値です。

説明する(単に):

  • OPに示すように、タイトルは明示的に印刷されます。

  • @Aこの関数を使用して配列を印刷するときは、配列を消去して各行の配列を補充しますsplice

  • 配列はゼロインデックス付き@F入力レコードのフィールドを格納します。$_

  • 配列の最初の要素(0番目の要素ではない)が@A配列の前面から移動します@F。から新しいレコードを読み取るたびに@F配列が作成されますperl。これは$1, $2, $3, ..., $NFのフィールドに似ていますawk

  • 配列の残りの部分は、配列の対応する合計を累積する@Aバイナリ要素インデックスです。(0|1)+1=>(1|2)@F

それが明確であることを願っています。

答え2

状況を理解するのに役立つように、かなり長い変数名と中間変数を使用してください(各行をコメントアウトしたり、後でコメントを追加する代わりに)。

$ cat tst.awk
BEGIN { FS=OFS="\t" }
NR==1 {
    for (inFldNr=2; inFldNr<=NF; inFldNr++) {
        fldName = $inFldNr
        if ( !(fldName in fldName2outFldNr) ) {
            outFldNr2name[++numOutFlds] = fldName
            fldName2outFldNr[fldName] = numOutFlds
        }
        outFldNr = fldName2outFldNr[fldName]
        out2inFldNrs[outFldNr,++numInFlds[outFldNr]] = inFldNr
    }

    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        outFldName = outFldNr2name[outFldNr]
        printf "%s%s", outFldName, (outFldNr<numOutFlds ? OFS : ORS)
    }
    next
}
{
    printf "%s%s", $1, OFS
    for (outFldNr=1; outFldNr<=numOutFlds; outFldNr++) {
        sum = 0
        for (inFldIdx=1; inFldIdx<=numInFlds[outFldNr]; inFldIdx++) {
            inFldNr = out2inFldNrs[outFldNr,inFldIdx]
            sum += $inFldNr
        }
        printf "%s%s", sum, (outFldNr<numOutFlds ? OFS : ORS)
    }
}

$ awk -f tst.awk file
var     x       y
a       2       2
b       2       2
c       1       1

答え3

GNUの使用datamash:

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose
  • スペースを区切り文字としてファイルを置き換える(-W
  • 最初のフィールドを-g1グループ化し()、-sグループ化する前にソートし()、2〜4フィールドの値を合計します。最初の入力行は列見出しとして処理され、出力(--headers)に印刷されます。
  • 結果を置き換え、タブ文字の代わりにスペース文字を区切り文字として使用します。

出力:

GroupBy(var) x y
sum(a) 2 2
sum(b) 2 2
sum(c) 1 1

結果を印刷するには、次のコマンドを使用しますsed

<file datamash -W transpose | 
  datamash -W --headers -s -g1 sum 2-4 |
  datamash --output-delimiter=' ' transpose | 
  sed 's/^[^(]*(\([^)]*\))/\1/'

出力:

var x y
a 2 2
b 2 2
c 1 1

次のcutコマンドを使用して、入力ファイルから最初の9列を削除して結果に再度追加できますpaste

paste -d ' ' <(cut -d' ' -f-9 file) <(cut -d' ' -f10- file | datamash ... )

head -n1printを使用して追加のヘッダー行をスキップできますtail -n+2

{
  head -n1 file 
  paste -d ' ' <(tail -n+2 file | cut -d' ' -f-9) <(tail -n+2 file | cut -d' ' -f10- | datamash ... )
} 

関連情報