Быстрая альтернатива grep -f

file.contain.query.txt

ENST001

ENST002

ENST003

file.to.search.in.txt

ENST001  90

ENST002  80

ENST004  50

Поскольку ENST003 не имеет записи во втором файле, а ENST004 не имеет записи в 1-м файле, ожидаемый результат:

ENST001 90

ENST002 80

Чтобы выполнить grep-запрос в конкретном файле, мы обычно делаем следующее:

grep -f file.contain.query <file.to.search.in >output.file

так как у меня есть 10000 запросов и почти 100000 raw в файле .to.search.in, это занимает очень много времени (например, 5 часов). Есть ли быстрая альтернатива grep -f?

Ответ 1

Если вам нужен чистый параметр Perl, прочитайте ключи файла запроса в хеш-таблице, затем проверьте стандартный ввод на эти клавиши:

#!/usr/bin/env perl
use strict;
use warnings;

# build hash table of keys
my $keyring;
open KEYS, "< file.contain.query.txt";
while (<KEYS>) {
    chomp $_;
    $keyring->{$_} = 1;
}
close KEYS;

# look up key from each line of standard input
while (<STDIN>) {
    chomp $_;
    my ($key, $value) = split("\t", $_); # assuming search file is tab-delimited; replace delimiter as needed
    if (defined $keyring->{$key}) { print "$_\n"; }
}

Вы бы использовали его так:

lookup.pl < file.to.search.txt

Хэш-таблица может занимать достаточное количество памяти, но поиск выполняется намного быстрее (поиск в хеш-таблице выполняется в постоянное время), что удобно, поскольку у вас есть в 10 раз больше ключей для поиска, чем для хранения.

Ответ 2

Если у вас есть фиксированные строки, используйте grep -F -f. Это значительно быстрее, чем поиск в регулярном выражении.

Ответ 3

Этот код Perl может помочь вам:

use strict;
open my $file1, "<", "file.contain.query.txt" or die $!;
open my $file2, "<", "file.to.search.in.txt" or die $!;

my %KEYS = ();
# Hash %KEYS marks the filtered keys by "file.contain.query.txt" file

while(my $line=<$file1>) {
    chomp $line;
    $KEYS{$line} = 1;
}

while(my $line=<$file2>) {
    if( $line =~ /(\w+)\s+(\d+)/ ) {
        print "$1 $2\n" if $KEYS{$1};
    }
}

close $file1;
close $file2;

Ответ 4

Если файлы уже отсортированы:

join file1 file2

если нет:

join <(sort file1) <(sort file2)

Ответ 5

Если вы используете Perl-версию 5.10 или новее, вы можете присоединиться к термину "запрос" в регулярное выражение с условиями запроса, разделенными "трубой". (Например: ENST001|ENST002|ENST003) Perl создает "trie", который, подобно хэшу, выполняет поиск в постоянное время. Он должен работать так же быстро, как решение, используя хеш-поиск. Просто чтобы показать другой способ сделать это.

#!/usr/bin/perl
use strict;
use warnings;
use Inline::Files;

my $query = join "|", map {chomp; $_} <QUERY>;

while (<RAW>) {
    print if /^(?:$query)\s/;
}

__QUERY__
ENST001
ENST002
ENST003
__RAW__
ENST001  90
ENST002  80
ENST004  50

Ответ 6

Mysql:

Импортирование данных в Mysql или подобное приведет к огромному улучшению. Будет ли это осуществимо? Результаты можно увидеть за несколько секунд.

mysql -e 'select search.* from search join contains using (keyword)' > outfile.txt 

# but first you need to create the tables like this (only once off)

create table contains (
   keyword   varchar(255)
   , primary key (keyword)
);

create table search (
   keyword varchar(255)
   ,num bigint
   ,key (keyword)
);

# and load the data in:

load data infile 'file.contain.query.txt' 
    into table contains fields terminated by "add column separator here";
load data infile 'file.to.search.in.txt' 
    into table search fields terminated by "add column separator here";

Ответ 7

use strict;
use warings;

system("sort file.contain.query.txt > qsorted.txt");
system("sort file.to.search.in.txt  > dsorted.txt");

open (QFILE, "<qsorted.txt") or die();
open (DFILE, "<dsorted.txt") or die();


while (my $qline = <QFILE>) {
  my ($queryid) = ($qline =~ /ENST(\d+)/); 
  while (my $dline = <DFILE>) {
    my ($dataid) = ($dline =~ /ENST(\d+)/);
    if ($dataid == $queryid)   { print $qline; }
    elsif ($dataid > $queryid) { break; } 
  }
}