3

大量のテキストを含む 2 つの大きなファイルがあり、ファイル B のフィールドと一致するフィールドを持つファイル A のすべての行を保持する必要があります。

ファイルAは次のようなものです:

Name (tab)  #  (tab)  #  (tab)  KEYFIELD  (tab)  Other fields

ファイルBIは、カットとsedなどを使用して、基本的にリストである1つのフィールドにまとめました。

そのため、目標は、その行のフィールドがファイル B の行の 1 つと一致する場合、ファイル A のすべての行を 4 番目のフィールド (KEYFIELD と表示されます) に保持することです (完全に一致する必要はないため、ファイル B が何とかしてファイルAは何とか何とか言った、それは大丈夫だろう)

私はやろうとしました:

grep -f fileBcutdown fileA > outputfile

編集:わかりました、私はあきらめます。強引に倒しただけです。

これを行うより良い方法はありますか?ファイル A は 13.7MB で、切り詰めた後のファイル B は 32.6MB です。

編集: これは、ファイル A の行の例です。

chr21 33025905 33031813 ENST00000449339.1 0 - 33031813 33031813 0 3 1835,294,104, 0,4341,5804,

ファイル B の行の例:

ENST00000111111
4

4 に答える 4

3

基本的なシェル ツールの使用制限に達しています。1 行あたり約 40 文字と仮定すると、ファイル A には 400,000 行、ファイル B には約 1,200,000 行が含まれます。基本的に、ファイル A の各行に対して grep を実行し、実行ごとに 1,200,000 行を grep で処理します。それはあなたが解析している480億行です。Unix ツールは驚くほど高速ですが、4,800 億回高速に実行されたものでも足し合わせることができます。

Perl や Python などの完全なプログラミング スクリプト言語を使用する方がよいでしょう。ファイル Bのすべての行をハッシュに入れます。ファイル A の各行を取得し、その 4 番目のフィールドがハッシュ内の何かと一致するかどうかを確認します。

数十万行で読む?10,000,000 エントリのハッシュを作成しますか? Perl は、それらの両方をほんの数分で解析できます。

何か - 私の頭の上から。スペクトについてはあまり教えてくれなかったので、テストはしませんでした:

#! /usr/bin/env perl

use strict;
use warnings;
use autodie;
use feature qw(say);

# Create your index
open my $file_b, "<", "file_b.txt";
my %index;

while (my $line = <$file_b>) {
    chomp $line;
    $index{$line} = $line;    #Or however you do it...
}
close $file_b;


#
# Now check against file_a.txt
#

open my $file_a, "<", "file_a.txt";
while (my $line = <$file_a>) {
    chomp $line;
    my @fields = split /\s+/, $line;
    if (exists $index{$field[3]}) {
         say "Line: $line";
    }
}
close $file_a;

ハッシュは、file_b を 400,000 回ではなく 1 回だけ読み取る必要があることを意味します。プログラムを開始し、オフィスのキッチンからコーヒーを飲みに行きます。(うーん! 乳成分不使用のクリーマー!) デスクに戻る頃には完成しているでしょう。

于 2012-09-21T20:09:21.220 に答える
2

を使用する 1 つの方法を次に示しGNU awkます。次のように実行します。

awk -f script.awk fileB.txt fileA.txt

の内容script.awk:

FNR==NR {
    array[$0]++
    next
}

{
    line = $4
    sub(/\.[0-9]+$/, "", line)
    if (line in array) {
        print
    }
}

または、ここにワンライナーがあります:

awk 'FNR==NR { array[$0]++; next } { line = $4; sub(/\.[0-9]+$/, "", line); if (line in array) print }' fileB.txt fileA.txt

GNU awkと を使用しfileB.txtて説明した の前処理を実行することもできます。これを上記のスクリプトに組み込みたい場合は、この行がどのように見えるかの例を提供する必要があります。cutsed


ファイルHumanGenCodeV12とを使用して更新GenBasicV12:

次のように実行します。

awk -f script.awk HumanGenCodeV12 GenBasicV12 > output.txt

の内容script.awk:

FNR==NR {
    gsub(/[^[:alnum:]]/,"",$12)
    array[$12]++
    next
}

{
    line = $4
    sub(/\.[0-9]+$/, "", line)
    if (line in array) {
        print
    }
}

これにより、 にある の行が正常に出力GenBasicV12されHumanGenCodeV12ます。出力ファイル ( output.txt) には 65340 行が含まれます。スクリプトは 10 秒もかからずに完了します。

于 2012-09-22T06:42:09.610 に答える
0

grep -f中サイズのパターン ファイル (< 1MB) でも非常に遅いようです。入力ストリームの各行に対してすべてのパターンを試すと思います。

私にとってより高速な解決策は、while ループを使用することでした。これは、それがかなり小さいことを前提としてfileAいるため(例では小さい方です)、大きいファイルを複数回反復するよりも、小さいファイルを複数回反復する方が望ましいです。

while read line; do
  grep -F "$line" fileA
done < fileBcutdown > outputfile

このループは、複数のパターンに一致する場合、行を複数回出力することに注意してください。この制限を回避するには を使用しますsort -uが、これはかなり遅くなる可能性があります。やってみなきゃ。

while read line; do
  grep -F "$line" fileA
done < fileBcutdown | sort -u | outputfile

行の順序に依存する場合は、を使用する以外に選択肢はないと思いますgrep -f。しかし、基本的には m*n のパターンマッチを試すことになります。

于 2012-09-21T19:39:57.070 に答える
0

以下のコマンドを使用します。

awk 'FNR==NR{a[$0];next}($4 in a)' <your filtered fileB with single field> fileA
于 2012-09-26T02:31:02.990 に答える