두 개의 탭으로 구분된 파일(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를 처음 접했지만 많은 조사를 했고 이와 유사한 작업을 수행하는 많은 예를 찾았습니다. 그리고 제 코드는 제가 본 다른 예와 매우 유사해 보입니다.
그러나 동일한 위치에 있지 않고 일치하지 않는 열도 유지하는 파일 사이에 두 개의 해당 키 행이 있는 예를 찾지 못했습니다.
이는 각각 고유한 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