두 개의 파일이 있습니다.
파일 1
NC_000001.11_NM_001005484.2 69270 234 69037
NC_000001.11_NM_001005484.2 69511 475 69037
NC_000001.11_NM_001005484.2 69761 725 69037
NC_000001.11_NM_001385640.1 942155 20 942136
파일 2
NC_000001.11_NM_001005484.2 65565 9
NC_000001.11_NM_001005484.2 69037 969
NC_000001.11_NM_001385640.1 924432 517
NC_000001.11_NM_001385640.1 925922 92
NC_000001.11_NM_001385640.1 930155 182
NC_000001.11_NM_001385640.1 931039 51
NC_000001.11_NM_001385640.1 935772 125
NC_000001.11_NM_001385640.1 939040 90
NC_000001.11_NM_001385640.1 939272 141
NC_000001.11_NM_001385640.1 941144 163
NC_000001.11_NM_001385640.1 942136 116
NC_000001.11_NM_001385640.1 942410 79
NC_000001.11_NM_001385640.1 942559 500
NC_000001.11_NM_001385640.1 943253 125
NC_000001.11_NM_001385640.1 943698 111
NC_000001.11_NM_001385640.1 943908 243
파일 1의 열 1이 파일 2의 열 1과 일치하고 파일 2의 열 2의 값이 파일 1의 열 4의 값보다 작은 경우 파일 2의 열 3을 일치하는 키와 일치시키려고 합니다. 그런 다음 file1의 각 줄과 file2의 해당 합계를 인쇄하고 싶습니다.
예상 출력
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
나는 이 작업을 수행할 만큼 awk 또는 Python에 대한 경험이 없지만 지금은 며칠 동안 이것저것 만져보고 있습니다. 도움을 주시면 감사하겠습니다.
답변1
$4
이 작업을 위해서는 행 1의 각 키( )에 대한 값을 저장해야 합니다. 아래 스크립트에서는 $1
이를 위해 호출된 배열을 키와 값으로 사용합니다.keys
$1
$4
또한 각 실제 행을 다른 배열에 저장해야 합니다( lines
행 번호를 키로 사용하고 전체 행을 이에 대한 값으로 사용하겠습니다). 이것이 큰 경우 많은 메모리를 소비할 수 있다는 점에 유의하십시오 file1
. 그러나 매우 크지 않은 한 RAM이 많은 최신 시스템에서는 문제가 되지 않을 것입니다. 너무 커서 RAM에 맞지 않는 경우 배열에 저장하는 대신 첫 번째 파일을 다시 반복하도록 스크립트를 수정해야 합니다 lines
.
linekeys
마지막으로 각 행 번호에 해당하는 키($1)도 저장해야 합니다 . 행 번호를 인덱스로, 키를 $1
값으로 사용하여 이를 위해 호출된 배열을 사용하겠습니다 . 그런데 첫 번째 파일이 너무 커서 두 번째로 처리해야 하는 경우 $1
각 행을 다시 처리하면서 가져올 수 있으므로 이 배열이 필요하지 않습니다. 기술적으로 이 배열은 필요할 때 블록에서 가져올 수 있으므로 실제로는 필요하지 않지만 split()
더 쉽습니다 lines[l]
. 더 간단한 코드와 잠재적으로 더 빠른 런타임을 위해 더 많은 메모리 사용량을 교환하는 것입니다.END{}
awk '# process the first file
NR==FNR {
keys[$1] = $4; # remember the value of $4 for the key ($1)
lines[FNR] = $0; # store the entire line
linekeys[FNR] = $1; # remember the key for that line
next
};
# process any remaining file(s)
$1 in keys {
if ($2 < keys[$1]) {
sum[$1]+=$3
};
};
# All files have been processed, so print the output
END {
for (l in lines) {
print lines[l], sum[linekeys[l]]
}
}' file1 file2
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
그런데, 이것을 두 sh
스크립트 중 하나에 유지하는 것이 좋습니다( this "$@"
대신 인수 로 사용하는 경우는 제외 하고 실행할 때 명령줄에서 입력 줄을 지정할 수 있습니다(예: ,awk
file1 file2
bash scriptname.sh file1 file2
또는로 awk
사용할 수 있도록 awk 스크립트(명령, 작은따옴표 및 파일 이름 제거)로 저장합니다 awk -f scriptname.awk file1 file2
.#!
첫 번째줄을 실행하면 실행 시 명령줄에 인터프리터 이름을 입력하지 않고도 직접 실행할 수 있도록 실행 가능하게 만들 수도 있습니다.
또는 정말로 주장한다면 전체 스크립트를 한 줄로 압축할 수 있습니다. 이를 달성하려면 문 사이에 필요한 곳에 세미콜론을 남겨 두십시오. 하지만 쉘 명령줄은 이렇게 짧은 스크립트라도 편집하기에 끔찍한 장소이고 심지어 Ctrl-XCtrl-E현재 줄이나 즐겨 사용하는 편집기를 편집할 수 있는 bash 와 같은 편리한 기능도 있기 때문에 권장하지 않습니다 vi
.
답변2
배열의 배열을 처리하려면 GNU awk를 사용하십시오.
$ cat tst.awk
NR==FNR {
addends[$1][$2][$3]
next
}
$1 in addends {
sum = 0
for ( val in addends[$1] ) {
if ( val < $4 ) {
for ( addend in addends[$1][val] ) {
sum += addend
}
}
}
print $0, sum
}
$ awk -f tst.awk file2 file1
NC_000001.11_NM_001005484.2 69270 234 69037 9
NC_000001.11_NM_001005484.2 69511 475 69037 9
NC_000001.11_NM_001005484.2 69761 725 69037 9
NC_000001.11_NM_001385640.1 942155 20 942136 1361
위의 내용은 file1 에 나타나는 것과 동일한 순서로 단순히 file1 의 행을 출력한다는 점에 유의하세요. 메모리에 쓰는 file1
대신 읽는 다른 file2
솔루션은 이를 수행하지 않을 수 있습니다. 예를 들어 for (i in array)
를 사용한 후에 인쇄하면 "random"으로 섞이게 됩니다. " 순서는 사용 중인 awk 버전의 내부에 따라 결정됩니다.https://www.gnu.org/software/gawk/manual/gawk.html#Scanning-an-Array, 따라서 일부 특정 예제 입력에 대해 예상되는 출력을 얻는 경우에도 모든 입력에 대해 항상 발생하는 결과에 의존하지 마십시오.