同じ列ヘッダーを共有するファイルの複数の列をマージしたいと思います。ファイルは次のようになり、タブで区切ったり、別の方法で区別したりできます。
AaBbN CcDdEeN FfN AaBbN FfN
1 5 4
3 1 2
2 NA 1
1 3
3 2
NA 4
したがって、フィールドに数字または文字列「NA」があります。結果は次のとおりです。
AaBbN CcDdEeN FfN
1 5 4
3 1 2
2 NA 1
1 3
3 2
NA 4
順序付けされていない列が多いため、ヘッダーヘッダーを毎日手動で指定するのではなく、自動的に読み取る必要があります。空のフィールドもたくさんあります。私はこれを行うために研究して注文してきpaste
ました。私の列が同じファイルにある間に別のファイルで動作することを除いて、join
特にjoin
必要なことをするようです。
そのため、列を別々のファイルに分割してから、ここから派生したコマンドを使用してjoin
接続してみました。awk
awk ' { for( i = 1; i <= NF; i++ ) printf( "%s\n", $(i) ) >i ".txt"; } ' file.txt
これにより別の列が提供されますが、ここで最初の問題が発生します。ヘッダーとデータの間にスペースがあるすべての列は正しく処理されませんでした。代わりに、これらのファイルには列ヘッダーしかありません。
2番目の質問は、join
ファイルを再マージしようとすると、入力がソートされていないためにエラーが発生することです。もちろんこれは不可能です。どんなソートでも私が維持していた関係を破壊します。
だから私は今行き止まった路地に閉じ込められています。ファイルから直接列をマージするより便利な方法はありますか?
編集する:
AdminBeesソリューションはトラブルシューティングに最も近いですが、結果は正しくありません。上記の例にawkスクリプトを適用した結果は次のとおりです。すべての項目がタブで区切られていることを確認しましたsed -i "s/[[:space:]]/ /g"
(タブを挿入するにはCTRL + VとTABを使用してください)。
AaBbN CcDdEeN FfN FfN
1 5 4
3 1 2
2 NA 1
1
3
NA
答え1
入力内容がタブ区切りの場合:
awk -F"\t" '
NR == 1 {for (i=1; i<=NF; i++) COL[i] = $i
}
{for (i=1; i<=NF; i++) OUT[NR, COL[i]] = $i
}
END {for (n=1; n<=NR; n++) {split ("", DUP)
for (i=1; i<=NF; i++) if (!DUP[COL[i]]++) printf "%s" FS, OUT[n,COL[i]]
printf RS
}
}
' file
A B C
1 5 4
3 1 2
2 2 1
1 3
3 2
1 4
後で部分索引として使用するために列ヘッダーを保存し、各行の値を行番号とヘッダー部分索引で索引付けされた配列に収集します。このEND
セクションでは、配列を元の順序で印刷し、重複した列ヘッダーを処理します。
より複雑なファイル構造の場合、反復処理が主な作業になる可能性があります。
答え2
タブで区切られた入力の場合。
入力ファイルに示すように、ヘッダーとその列番号を配列に読み込み、各列の入力ファイルを同じ headerName を持つ同じファイル名 headerName.txt に分割します。結局一緒に貼り付けてcolumn
出力を美しくするコマンド。
awk -F'\t' '
## find all the column number(s) when same header found and store in `h` array
## key is the column number and value is header name. for an example:
## for the header value 'A', keys will be columns 1 &4
NR==1{ while (++i<=NF) h[i]=$i; next; }
{ for (i=1; i<=NF; i++) {
## save the field content to a file which its key column matches with the column
## number of the current field. for an example:
## for the first field in column 1; the column number is 1, and so 1 is the key
## column for header value A, so this will be written to "A.txt" filename
## only if it was not empty.
if ($i!=""){ print $i> h[i]".txt" };
}; }
## at the end paste those all files and beautify output with `column` command.
## number of .txt files above is limit to the number of uniq headers in your input.
END{ system("paste *.txt |column \011 -tn") }' infile
コメントアウトされていないコマンド:
awk -F'\t' '
NR==1{ while (++i<=NF) h[i]=$i; next; }
{ for (i=1; i<=NF; i++) {
if ($i!=""){ print $i> h[i]".txt" };
}; }
END{ system("paste *.txt |column \011 -tn") }' infile
答え3
ファイル全体を「バッファリング」する必要がない少し異なるアプローチ:
AWKスクリプトcolmerge.awk
:
FNR==1{
for (i=1; i<=NF; i++)
{
hdr[i]=$i;
if (map[$i]==0) {map[$i]=i; uniq_hdr[++u]=$i; printf("%s",$i);}
if (i==NF) printf("%s",ORS); else printf("%s",OFS);
}
}
FNR>1{
delete linemap;
for (i=1; i<=NF; i++) if ($i!="") linemap[hdr[i]]=$i;
for (i=1; i<=u; i++)
{
printf("%s",linemap[uniq_hdr[i]]);
if (i==u) printf("%s",ORS); else printf("%s",OFS);
}
}
使用
awk -F'\t' -v OFS='\t' -f colmerge.awk file
これにより、すべてのヘッダーを収集し、「固有」ヘッダーと1行目の最初の出現を識別し、連続する各行のヘッダーとnull以外の値の間にマップを作成し、「固有」をクリックします。ヘッダーの順序は、次の場合に認識されるとおりに印刷されます。最初の行を処理しています。
ただし、これは入力ファイルがタブで区切られている場合にのみ機能します。これは、「空」フィールドを安定して検出する唯一の方法であるためです。
delete
また、すべての実装が配列全体のステートメントをサポートするわけではありません(ただし、およびでは動作しなければなりません)。linemap
awk
gawk
mawk
nawk