キーが他のファイルと一致し、条件に基づいてファイルの値を合計します。

キーが他のファイルと一致し、条件に基づいてファイルの値を合計します。

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の"$@"代わりに引数として使用する場合を除き、実行時にコマンドラインで入力行を指定できます(例:awkfile1 file2bash 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したがって、特定の例の入力に期待される出力を取得しても、すべての入力に対して常に発生する結果に依存しないでください。

関連情報