awk/join을 사용하여 열을 기반으로 항목 조인

awk/join을 사용하여 열을 기반으로 항목 조인

파이프로 구분된 두 개의 파일이 있고 열 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

두 번째 항목 file1의 경우(두 파일 모두에 일치하는 column1+column2가 없으면 foo에 대한 누락된 항목을 0으로 바꿉니다. 그 반대도 마찬가지입니다)

1111|BBB|30|0

파일 2에는 있지만 파일 1에는 없는 항목 키(열 1 + 열 2)의 경우(이것은 파일 2의 항목 3에 대한 예상 출력입니다.)

3333|AAA|0|40

따라서 원하는 출력의 전체 형식은 두 파일 모두에서 column1+column2로 표시되는 모든 고유 키를 나열하는 것입니다. 세 번째 열 항목은 파일 1의 열 4에 있는 값(파일 1에 값이 없는 경우 0)이고, 출력의 네 번째 열은 파일 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을 추가합니다... 모든 시나리오에서 작동하도록 하려면 어떻게 해야 합니까?

위 명령은 두 파일의 열 1(column1+column2)에 키를 추가한 다음 해당 새 키를 기반으로 조인하여 일부 절차적 대체를 수행합니다. -e0 키가 file1에는 있지만 file2에는 없으면 0이 추가됩니다. 새 키(column1-column2)가 파일 2에는 있지만 파일 1에는 없는 경우를 처리하려면 어떻게 해야 합니까?

답변1

귀하의 방법을 사용하면 join두 번 (또는join한 번의 호출로 방법을 변경하세요.):

  • 공통 라인 및 페어링할 수 없는 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예를 들어 로 인덱스된 두 배열에 저장한 $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

다음은 |두 파일 중 첫 번째 파일을 @(파일의 다른 곳에 나타나지 않는 문자로)로 바꾸고 실행한 join후 다시 @원래 의 |. 이런 방식으로 |원본 파일의 열 1과 2를 포함하는 새로운 구분된 조인 필드를 만듭니다 .

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

출력 필드 사양( -o)에서 0은 조인 필드를 나타내고 두 파일의 열 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|에프생산하다에스운영자).
  • {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에만 존재하는 레코드가 포함되어 있으므로 해당 키( y) 0가 file1에 존재하지 않고 해당 값이 file2에 존재하므로 인쇄합니다 f2[y].

관련 정보