2番目のファイルの情報に基づいて、最初のファイルの特定の行をどのように削除できますか?

2番目のファイルの情報に基づいて、最初のファイルの特定の行をどのように削除できますか?

次のファイルがあります。

ファイル1:

    1   2       0.000
    1   3       0.213
    1   4       0.014
    1   5       0.001
    1   6       0.555
    1   7       0.509
    1   8       0.509
    3   4       0.995
    3   5       0.323
    3   6       0.555
    3   7       0.225
    3   8       0.000
    4   5       0.095
    4   6       0.058
    4   7       0.335
    4   8       0.000
    5   6       0.995
    5   7       0.658
    5   8       0.000
    6   7       0.431
    6   8       0.000
    7   8       0.000

ファイル2:

1
3 
4
5 6 7

ファイル2で数字のペアが連続して観察されるファイル1の行を維持したいと思います。ここでは、ファイル2から5、6、7のみが観察されます。したがって、各数値ペアはファイル1に残る必要があります。したがって、出力は次のようになります。

5   6       0.995
5   7       0.658
6   7       0.431

どのような提案があります(実際のデータが大きく、数字が1から始まらない可能性があることを考慮)。

答え1

perl解決策が大丈夫なら。 file1の列1と列2のデータは、列1の値が常に列2よりも小さくなるようにソートされていると仮定します。

$ perl -lane '
    if(!$nf)
    {
        if($#F > 0)
        {
            foreach $i (0..$#F)
            {
                $h{"-$F[$i]-$F[$_]-"}++ foreach ($i+1..$#F)
            }
        }
        $nf++ if eof;
    }
    else
    {
        print if $h{"-$F[0]-$F[1]-"}
    }
' file2 file1
    5   6       0.995
    5   7       0.658
    6   7       0.431
  • まず、file2にペアでキーのハッシュを構築します(再度数字が昇順であると仮定)。
    • と台などの-不整合を防ぐために、2つの列の値を囲みます。11201120
  • 次に、file1 行のキーがある場合は、その行を印刷します。


file2に変更された場合

$ cat file2
1
3 4
5 6 7 8

$ perl -lane '
    if(!$nf)
    {
        if($#F > 0)
        {
            foreach $i (0..$#F)
            {
                $h{"-$F[$i]-$F[$_]-"}++ foreach ($i+1..$#F)
            }
        }
        $nf++ if eof;
    }
    else
    {
        print if $h{"-$F[0]-$F[1]-"}
    }
' file2 file1
    3   4       0.995
    5   6       0.995
    5   7       0.658
    5   8       0.000
    6   7       0.431
    6   8       0.000
    7   8       0.000

答え2

awkを使用してください。

最初の awk コマンドは、すべてのペアを含むファイルを生成します。 2番目のawkコマンドは、ペアファイルを配列に一度読み込み、一致するすべての行を印刷します。

awk 'NF>1{for(i=1;i<=NF;i++) for(j=i+1;j<=NF;j++) print $i,$j;}' file2 > /tmp/pairs
awk 'BEGIN{while((getline <"/tmp/pairs") > 0) pair[$1,$2]=1; close("/tmp/pairs")} ($1,$2) in pair' file1

2番目のコマンドには多くのメモリが必要な場合があります!ファイルが順番に並べられている場合は、何らかの方法で配列を避け、両方のファイルを同時に読み取ることができます。最終実行前にペアファイルを表示するには、2つのコマンドを使用します。

ここのコードは単一のコマンドと同じで読みやすい形式です。

awk '
  BEGIN {
    f="file2"
    while((getline <f) > 0)
      for(i=1;i<=NF;i++)
        for(j=i+1;j<=NF;j++)
          pair[$i,$j]=1;
    close(f);
  }
  ($1,$2) in pair
' file1

参考までにfile1(22 mill.lines)、file2(4 mill.lines)に対していくつかのベンチマークを実行して2 mill.linesを作成しました。ライン出力。

  • gawk:9.6秒、275Mメモリ
  • mawk:20.7秒、134Mメモリ
  • SundeepのPerl回答:31.9秒、231Mメモリ

関連情報