2つのファイルがあります。
ファイル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
値として使用し、そのために呼び出された配列を使用します。しかし、最初のファイルが大きすぎて2番目に処理する必要がある場合は、$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
しかし、これを2つのsh
スクリプトのうちの1つに保持することをお勧めします(thisの"$@"
代わりに引数として使用する場合を除き、実行時にコマンドラインで入力行を指定できます(例:awk
file1 file2
bash scriptname.sh file1 file2
またはとしてawk
使用できるように、awkスクリプト(コマンド、一重引用符、およびファイル名の削除)として保存しますawk -f scriptname.awk file1 file2
。#!
最初行を実行すると、実行時にコマンドラインにインタプリタ名を入力せずに直接実行できるように実行可能にすることもできます。
あるいは、本当に主張している場合は、スクリプト全体を1行に圧縮できます。これを達成するには、ドアの間に必要な場所にセミコロンを残します。しかし、シェルのコマンドラインは、このような短いスクリプトでも編集するのにひどい場所であり、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)
を使用した後に印刷すると、「ランダム」で混ざります。順序は、使用しているawkバージョンの内部によって決まります。https://www.gnu.org/software/gawk/manual/gawk.html#Scanning-an-Arrayしたがって、特定の例の入力に期待される出力を取得しても、すべての入力に対して常に発生する結果に依存しないでください。