2つのタブで区切られたファイル(FileA.tsvとFileB.tsv)があります。
ファイルA.tsv
ID | 制度法 | 円 | いくつかの列があります... | 長さ |
---|---|---|---|---|
196-0 | 196 | 0 | ---- | 12874 |
195-1 | 195 | 1 | ---- | 12874 |
56-0 | 56 | 0 | ---- | 3349 |
115-1 | 115 | 1 | ---- | 5297 |
ファイルAには何百もの行と12の列がありますが、ここではすべて説明しません。 2と3の各値は一意ではありませんが、特定の組み合わせは一意です。したがって、event_idは、2と3の値を連結して形成される一意の識別子である。
ファイルB.tsv
列1 | 2列 | 3列 | いくつかの列があります... | 列16 |
---|---|---|---|---|
195 | 1 | 適用範囲 | ---- | CTTGCTTGAGCTGCTCTGCAA ... |
196 | 0 | 適用範囲 | ---- | TTCTAAAGTATAAAAGCCTGTC ... |
196 | 9 | 適用範囲 | --- | TTCTAAAGTATAAAAGCCTGTC ... |
196 | 11 | 適用範囲 | --- | ACATTTAAAGAATTGCTTAAG ... |
FileB にはヘッダーはありません。
列 2 と列 3 は、ファイル A の列 1 と列 2 の一部と一致します。同様に、1列と2列の値は一意ではありませんが、特定の組み合わせは一意です。 FileB に現れるすべての行は常に FileA の行と一致しますが、その逆ではありません。
awk を使用して、FileA の各行に FileB の $1 と $2 と一致する $2 と $3 があることを確認し、そうであれば FileA 行全体を印刷し、その FIleB の $16 値を行の末尾に追加したいと思います。それ以外の場合、FIleA 行はそのまま印刷されます。
予想出力(ファイルC):
ID | 制度法 | 円 | 複数列 | 長さ | 列16 |
---|---|---|---|---|---|
196-0 | 196 | 0 | ---- | 12874 | TTCTAAAGTATAAAAGCCTGTC ... |
195-1 | 195 | 1 | ---- | 12874 | CTTGCTTGAGCTGCTCTGCAA ... |
56-0 | 56 | 0 | ---- | 3349 | ---- |
115-1 | 115 | 1 | ---- | 5297 | ---- |
これまで私は以下を持っています:
awk -F "\t" 'NR==FNR {a[$1,$2]=($16); next} ($2,$3) in a {print $0, a[$16]}' FileB.tsv FileA.tsv > FileC.tsv
このコードは一致する行のみを提供しますが、一致する行の末尾に$ 16を追加しません。
空 | 空 | 空 | 空 | 空 |
---|---|---|---|---|
196-0 | 196 | 0 | ---- | 12874 |
195-1 | 195 | 1 | ---- | 12874 |
If-Elseステートメントを追加しようとすると、次のようになります。
awk -F "\t" 'NR==FNR {a[$1,$2]=($16); next} { if (($2,$3) in a) {print $0, a[$16]} else {print $0}}' FileB.tsv FileA.tsv > FileC.tsv
FileA のヘッダーと一致しない行を保存するために、出力は FileA です。
私は初めてawkに触れましたが、多くの調査を行い、同様のことを行う多くの例を見つけました。そして私のコードは私が見た他の例と非常によく似ています。
ただし、同じ場所に存在せず、一致しない列も保持するファイルの間に2つの対応するキー行がある例が見つかりませんでした。
これは、それぞれ一意のFileAセットとFileBセットを持つ複数のディレクトリのBashループを使用して実行されます。これに関して問題はない。すべてのディレクトリには独自の出力FileCがありますが、内容が間違っている可能性があります。
set -euo pipefail
IFS=$'\n\t'
for D in ~/Path/to/directories/with/tables/*; do
if [ -d "${D}" ]; then
cd "$D"
awk -F "\t" 'NR==FNR {a[$1,$2]=($16); next} { if (($2,$3) in a) {print $0, a[$16]} else {print $0}}' *_FileB.tsv *_FileA.tsv > "${D}".FileC.tsv
fi
done ```
Any help or correction will be greatly appreciated.
答え1
awkスクリプトは良いスタートですが、かなりマイナーな問題を解決する必要があります。
OFS
同様に、出力フィールド区切り記号()をタブに設定する必要がありますFS
。- 新しい列ヘッダーを印刷する必要があります。
- 一致する行で
a[$2,$3]
はなく印刷する必要があります。a[$16]
- 一致しない行を維持するには、その行も印刷する必要があり、すべての出力行が同じ数の列を持つように空のフィールドを追加することをお勧めします。
たとえば、
$ awk -F "\t" -v OFS='\t' '
NR == FNR { a[$1,$2] = $5; next };
FNR == 1 { c = "column 16" };
($2,$3) in a { c = a[$2,$3] };
{
print $0, c;
c = ""
}' FileB.tsv FileA.tsv
id graph circle several columns... length column 16
196-0 196 0 ---- 12874 TTCTAAAGTATAAAGCCTGTC...
195-1 195 1 ---- 12874 CTTGCTTGAGCTGCTCTGCAA...
56-0 56 0 ---- 3349
115-1 115 1 ---- 5297
a[$1,$2]=$5
FileBサンプルデータには5つのフィールドしかないので、ここではこれを使用しています。$16
実際のデータに変更してください。
これは変数を使用して追加c
する列の値を保持します。これには、新しい列名、空の文字列、または一致する行の列に追加される値が含まれます。各出力行が印刷された後、空の文字列にリセットされます。
cat -T
参考までに、空のフィールドは一般的には見えませんが、出力をパイピングして、一致しない行に空のフィールドが追加されていることを確認できます。^I
その行の末尾に(タブ)が表示されます.
ハードコードではなく、FileB.tsvの最初の行から列名を取得する代替バージョン:
$ awk -F "\t" -v OFS='\t' '
NR == 1 { c = $5 ; next };
NR == FNR { a[$1,$2] = $5; next };
($2,$3) in a { c = a[$2,$3] };
{ print $0, c; c = "" }' FileB.tsv FileA.tsv
答え2
awkを使用してください。
$ cat tst.awk
BEGIN {
FS = OFS = "\t"
}
NR == FNR {
val = $NF
if ( FNR == 1 ) {
hdr = val
}
else {
map[$1 FS $2] = val
}
next
}
{
if ( FNR == 1 ) {
val = hdr
}
else {
key = $2 FS $3
val = (key in map ? map[key] : "----")
}
print $0, val
}
$ awk -f tst.awk FileB.tsv FileA.tsv
id graph circle several columns... length Column16
196-0 196 0 ---- 12874 TTCTAAAGTATAAAGCCTGTC...
195-1 195 1 ---- 12874 CTTGCTTGAGCTGCTCTGCAA...
56-0 56 0 ---- 3349 ----
115-1 115 1 ---- 5297 ----
入力の最後の列でない場合は、$NF
次に変更します。$16
$16