2つのファイル間で順次置換された行のインデックスを抽出します。

2つのファイル間で順次置換された行のインデックスを抽出します。

タブで区切られた2つの大きなファイル(> 10 GB)があり、並べ替えるとその内容が同じであることがわかります。

Sourceただし、同じ「キー」(ここでキーは列に基づいてグループ化されたLocation行として定義されています)を共有するときに、行の順序と置き換えられた行のインデックスに興味があります。

つまり、これら2つのファイル間の行は、同じグループに属している場合にのみ(つまり、同じソースと場所を共有する場合)、互いに比較する必要があります。

たとえば、次の例では、行4、5、6をfile1.tsv次の行4、5、6と比較する必要があります。file2.tsv

注:ファイルは通常のTSVです。より見やすくするために、ここに余分なスペースを追加し、列を中央に揃えて右に揃えます。このスペースは元のファイルの一部ではありません。

ファイル1.tsv

     Identifier  Position Source  Location
     AY1:2301        87    ch1        14
    BC1U:4010       105    ch1        14
    AC44:1230        90    ch1        15
    AJC:93410        83    ch1        16
    ABYY:0001       101    ch1        16
       ABC:01        42    ch1        16
      HH:A9CX       413    ch1        17
      LK:9310         2    ch1        17
    JFNE:3410       132    ch1        18
    MKASDL:11        14    ch1        18
   MKDFA:9401        18    ch1        18
  MKASDL1:011       184    ch2        50
   LKOC:AMC02        18    ch2        50
     POI:1100       900    ch2        53
    MCJE:09HA        11    ch2        53
   ABYCI:1123        15    ch2        53
     MNKA:410         1    ch2        53

ファイル2.tsv

     Identifier  Position Source  Location
     AY1:2301        87    ch1        14
    BC1U:4010       105    ch1        14
    AC44:1230        90    ch1        15
       ABC:01        42    ch1        16
    ABYY:0001       101    ch1        16
    AJC:93410        83    ch1        16
      HH:A9CX       413    ch1        17
      LK:9310         2    ch1        17
    MKASDL:11        14    ch1        18
    JFNE:3410       132    ch1        18
   MKDFA:9401        18    ch1        18
  MKASDL1:011       184    ch2        50
   LKOC:AMC02        18    ch2        50
     MNKA:410         1    ch2        53
     POI:1100       900    ch2        53
   ABYCI:1123        15    ch2        53
    MCJE:09HA        11    ch2        53

「diff」に似ていますが、「グループ」レベルで実行したいです(行がSource同じ合計を共有している場合にのみ比較されますLocation)。

抽出したいオリジナル同じ「ソース/位置」内で行の順序が「交換」されると、「行番号」グループ「(またはキー)。

行全体の内容が一致する必要があります。

しかし、どうすればいいのかわかりません。私が考えることができるのはforループを書くことだけです。これは、元のデータセットに数百万の行がある場合、非常に非効率的です。

予想される結果:

Group_Source:Location  df1.index  df2.index

ch1:16                         4          6
ch1:16                         6          4
ch1:18                         9         10
ch1:18                        10          9
ch2:53                        14         15
ch2:53                        15         17
ch2:53                        17         14

仮定:

  • 両方のデータフレームは同じ数の行を持ちます。
  • 両方のデータフレームは同じです(行の順序のみが変わるため、両方ともソース基準、位置基準、位置基準、識別子基準でソートされている場合はまったく同じです)。
  • 「交換された」行は、常にすべての列の内容と正確に一致します。

答え1

入力ファイルのサイズのため、これは私が使用できるまれなケースの1つなので、getline> 10Gの代わりに一度に数行だけメモリに保存します。

$ cat tst.awk
BEGIN {
    OFS = "\t"
    print "Group_Source:Location", "df1.index", "df2.index"
}
NR != FNR { exit }
{ srcLoc = $3 ":" $4 }
srcLoc != prevSrcLoc {
    if ( NR > 1 ) {
        diff()
    }
    prevSrcLoc = srcLoc
}
{
    file1[$1,$2] = FNR - 1
    if ( (getline < ARGV[2]) > 0 ) {
        file2[$1,$2] = FNR - 1
    }
}
END { diff() }

function diff(          idPos) {
    for ( idPos in file1 ) {
        if ( file1[idPos] != file2[idPos] ) {
            print prevSrcLoc, file1[idPos], file2[idPos]
        }
    }
    delete file1
    delete file2
}

$ awk -f tst.awk file1.tsv file2.tsv
Group_Source:Location   df1.index       df2.index
ch1:16  6       4
ch1:16  4       6
ch1:18  10      9
ch1:18  9       10
ch2:53  17      14
ch2:53  15      17
ch2:53  14      15

もっと情報がgetline欲しいなら読んでくださいhttp://awk.freeshell.org/AllAboutGetline

Identifier上記のコードは、2つのファイル間の4つのフィールドをすべて比較するため、入力中および/または繰り返される場合にも機能します。Position例の入力に示すように、ソースとロケーションの値が2つのファイル間で同じ順序であるとします。

答え2

たとえば、次のようになりますawk

$ awk '{ 
        if(FNR==1){
            next
        }
        else if(FNR==NR){
            a[$1]=FNR-1;
        } 
        else if ( a[$1] != FNR-1 ){
            print $3":"$4, FNR-1, a[$1]
        }
    }' file1.tsv file2.tsv 
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 17
ch2:53 15 14
ch2:53 17 15

説明する

  • if(FNR==1){ next }FNR現在読んでいるファイルの行番号(レコード番号)を保存します。したがって、これが入力ファイルの最初の行である場合は、ヘッダーを処理したくないので、スキップしてください。
  • else if(FNR==NR){ ... }NRどのファイルを読んでいるかに関係なく、現在の入力行番号を保存します。したがって、FNR同じNR場合、最初のファイルを読んでいるという意味です。
  • a[$1]=FNR-1: したがって、これが最初のファイルの場合は、最初のフィールドを連想配列のインデックス(キー)として保存し、その値は現在のファイルの行番号(FNR)になりますが、計算を望まないので1を減算します。ヘッダー。
  • else if ( a[$1] != FNR-1 ){:これはelse if以前のファイルに関連しているため、FNRこのファイルが同じではない場合にのみ入力されるため、NR2番目のファイルを読み取る場合にのみ入力されます。だから私たちが2番目のファイルを読むならそしてこの行の最初のフィールドの配列に格納されている値は、a現在のファイルの行番号から1を引いたものと等しくないため、印刷しようとしています。
  • print $3":"$4, FNR-1, a[$1]:したがって、3番目のフィールドa:と4番目のフィールドを印刷し、FNRマイナス1とa最初のフィールドの配列に格納されている値を印刷します。

最後に、パディングとヘッダーを使用してきれいに印刷するには、次のようにします。

$ awk 'BEGIN{
            printf "%-26s%-12s%-12s\n", \
                "Group_Source:Location","df1.index","df2.index"
        } 
        { 
            if(FNR==1){ next }
            else if(FNR==NR){ a[$1]=FNR-1 } 
            else if ( a[$1] != FNR-1){
                printf "%-26s%-12s%-12s\n", $3":"$4, FNR-1, a[$1]
            }
        }' file1.tsv file2.tsv 
Group_Source:Location     df1.index   df2.index   
ch1:16                    4           6           
ch1:16                    6           4           
ch1:18                    9           10          
ch1:18                    10          9           
ch2:53                    14          17          
ch2:53                    15          14          
ch2:53                    17          15          

重要:この方法を使用するには、最初のファイルの各行(ヘッダーを除く)ごとに少量のデータをメモリに保持する必要があります。これは大容量ファイルの場合に問題になる可能性がありますが、これらのタスクが実行される可能性のあるほとんどのコンピュータではそうでない可能性があります。これが問題だったらおすすめしたいエドの答えこれははるかに高速で、メモリの問題もないはずです。

関連情報