awk/joinを使用して列に基づいて項目を結合する

awk/joinを使用して列に基づいて項目を結合する

パイプで区切られた2つのファイルがあり、列1 +列2が両方のファイルで一致する可能性があります。あるいは、あるファイルには項目が含まれ、別のファイルには項目が含まれていない可能性があります。 $1"-"$2 と同じ一致キーを FS として取得するために '|'パイプを使用すると仮定します。

ファイル1

1111|AAA|foo|50
1111|BBB|foo|30
2222|BBB|foo|10

ファイル2

1111|AAA|bar|10
1111|CCC|bar|20
3333|AAA|bar|40

最初の項目に必要な出力は次のとおりです(これがあります)。

1111|AAA|50|10

2番目の項目file1の場合(両方のファイルに一致するcolumn1 + column2がない場合は、fooの欠落項目を0に置き換えます。逆も同様です)

1111|BBB|30|0

ファイル 2 にはあるがファイル 1 にはない項目キー (列 1 + 列 2) の場合 (これは、ファイル 2 の項目 3 に対する期待される出力です。)

3333|AAA|0|40

したがって、目的の出力の完全な形式は、両方のファイルでcolumn1 + column2として表示されるすべての一意のキーを一覧表示することです。 3番目の列項目はファイル1の列4の値(ファイル1に値がない場合は0)、出力の4番目の列はファイル2の列4の値(値がファイル1にない場合)です。ファイル2値の場合は0))です。

研究もたくさん試していろいろ試してみましたが、次のコマンドを使ってみると、file2にはcolumn1+column2ペアがあり、file1にないと私の値は出力されません。

join -t"|" -e0 -a1 -a2 -o 1.2,1.3,1.5,2.5 <(<file1 awk -F"|" '{print $1"-"$2"|"$0}' | sort -k1,1) <(<file2 awk -F"|" '{print $1"-"$2"|"$0}' | sort -k1,1)

file1にはcolumn1 + column2の一致がありますが、file2にはない場合、上記の場合は予想される出力を提供し、存在しない一致に0を追加します...すべてのシナリオで動作させるにはどうすればよいですか?

上記のコマンドは、2つのファイルの列1(column1 + column2)にキーを追加し、新しいキーに基づいて結合していくつかの手続き型置換を実行します。 -e0 キーが file1 にあるが file2 にない場合は 0 が追加されます。新しいキー(column1-column2)がファイル2にはあるがファイル1にはない場合を処理するにはどうすればよいですか?

答え1

あなたの方法を使用すると、join2回(またはjoin1回の呼び出しで方法を変更してください。):

  • 共通ラインとペアリングできないfile1ライン印刷join -t'|' -e0 -a1 -o 1.2,1.3,1.5,2.5 <(<file1 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1) <(<file2 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1)
  • file2ペアリングできないライン印刷join -t'|' -e0 -v2 -o 2.2,2.3,1.5,2.5 <(<file1 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1) <(<file2 awk -F'|' '{print $1"-"$2"|"$0}' | sort -t'|' -k1,1)

awk単一の呼び出しで同じ操作を実行できます。$4たとえば、 で索引付けされた 2 つの配列に保存して$1|$2から、ENDブロック内の各配列索引を繰り返し比較し、それに応じて印刷できます。

awk -F'|' 'NR==FNR{z[$1"|"$2]=$4;next}{x[$1"|"$2]=$4}
END{for (j in x){if (!(j in z)){print j, "0", x[j]}};
for (i in z){if (i in x){print i, z[i], x[i]} else {print i, z[i], "0"}}
}' OFS="|"  file1 file2

答え2

次の|2つのファイルの最初のファイルを@(ファイルの他の場所に表示されない文字で)に置き換えて実行したjoin後、@元の元の|。このようにして、|元のファイルの列1と2を含む新しい区切り結合フィールドを作成します。

join -t'|' -e0 -a1 -a2 -o0,1.3,2.3 \
    <( sed 's/|/@/' file1 | sort )  \
    <( sed 's/|/@/' file2 | sort ) |
tr '@' '|'

出力フィールドの仕様(-o)では、0は結合フィールドを表し、2つのファイルの列3は実際には元のデータの列4です。

与えられた入力ファイルに対して以下を生成します。

1111|AAA|50|10
1111|BBB|30|0
1111|CCC|0|20
2222|BBB|10|0
3333|AAA|0|40

答え3

別のawk方法:

awk -F'|' 'NR==FNR{f1[$1FS$2]=$NF;next} {f2[$1FS$2]=$NF} 
    END{for (x in f1){print x,f1[x],f2[x]?f2[x]:0; delete f2[x]};
        for (y in f2) print y, 0, f2[y]
}' file[12] OFS='|'

説明する:

  • NR==FNR{f1[$1FS$2]=$NF;next}$1FS$2、これはfile1に対してのみ実行され、キーの組み合わせを使用すると、名前付き配列に最後の列値が格納されます(awkに置き換えられます)。$NFf1FS|F生産するSオペレーター)。
  • {f2[$1FS$2]=$NF}、上記と同じですが、file2に対してのみ実行されます。
  • for (x in f1){print x,f1[x],f2[x]?f2[x]:0; delete f2[x]}、配列を繰り返し、f1キー(x)、file1の対応する値を印刷f1[x]します。同じfile1キーがfile2に存在する場合はそれを印刷し、そうでない場合は0(三項条件を使用してf2[x]?f2[x]:0)印刷し、file2でも印刷します。同じレコードを削除します。キーdelete f2[x]
  • for (y in f2) print y, 0, f2[y]現在、配列f2にはfile2にのみ存在するレコードが含まれているため、対応するキー(y0はfile1に存在せず、その値はfile2に存在するため印刷しますf2[y]

関連情報