複数の行と列を持つファイルを取得し、一致するヘッダーを持つ列の値を水平方向に追加(合計)したいと思います。私のファイル(この例ではありません)は、最初の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 -n1
printを使用して追加のヘッダー行をスキップできますtail -n+2
。
{
head -n1 file
paste -d ' ' <(tail -n+2 file | cut -d' ' -f-9) <(tail -n+2 file | cut -d' ' -f10- | datamash ... )
}