AWK:2つのキー列がファイル間で一致する場合は、一致しない行を維持しながら、あるファイルの列16を別のファイルの一致する行に追加します。

AWK:2つのキー列がファイル間で一致する場合は、一致しない行を維持しながら、あるファイルの列16を別のファイルの一致する行に追加します。

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スクリプトは良いスタートですが、かなりマイナーな問題を解決する必要があります。

  1. OFS同様に、出力フィールド区切り記号()をタブに設定する必要がありますFS
  2. 新しい列ヘッダーを印刷する必要があります。
  3. 一致する行でa[$2,$3]はなく印刷する必要があります。a[$16]
  4. 一致しない行を維持するには、その行も印刷する必要があり、すべての出力行が同じ数の列を持つように空のフィールドを追加することをお勧めします。

たとえば、

$ 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]=$5FileBサンプルデータには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

関連情報