2012-07-15 22 views
10

file.contain.query.txtveloce alternativa al grep -f

ENST001 

ENST002 

ENST003 

file.to.search.in.txt

ENST001 90 

ENST002 80 

ENST004 50 

Perché ENST003 non ha voce nel 2 ° del file e ENST004 ha nessuna voce in prima file l'output previsto è:

ENST001 90 

ENST002 80 

a grep più query in un particolare file facciamo di solito la seguente:

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

poiché ho come 10000 query e quasi 100000 raw in file.to.search.in ci vuole molto tempo per finire (come 5 ore). C'è un'alternativa veloce a grep -f?

+0

Quali sono le tue esigenze? vuoi un file con le righe del secondo filtrato con le chiavi del primo? –

+0

Ho modificato i risultati previsti – user1421408

+1

Il reindirizzamento dell'input non è necessario. –

risposta

10

Se si desidera una soluzione pura Perl, leggere le chiavi di file query in una tabella hash, quindi controllare lo standard input contro quelle chiavi:

#!/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"; } 
} 

usereste in questo modo:

lookup.pl < file.to.search.txt 

una tabella hash può richiedere una discreta quantità di memoria, ma le ricerche sono molto più veloce (le ricerche della tabella hash sono in tempo costante), il che è utile poiché si hanno 10 volte più chiavi da cercare che memorizzare.

+2

Questa è la Ferrari quando confrontata con grep -f .. Grazie – user1421408

+0

Soluzione perfetta; +1 –

5

Questo codice può Perl aiuta a:

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; 
+0

Ti sei dimenticato di controllare il valore di ritorno delle syscalls. – tchrist

1

Mysql:

importazione dei dati in MySQL o simile fornirà un miglioramento immenso. Sarà fattibile? Potresti vedere i risultati in pochi secondi.

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"; 
+0

Non l'ho provato ma funzionerà con un po 'di modifiche a seconda della situazione. Ci vorrà pochissima memoria a meno che tu non voglia che sia basato su ram. –

0
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; } 
    } 
} 
6

Se è stato fissato stringhe, utilizzare grep -F -f. Questo è significativamente più veloce della ricerca regex.

5

Se i file sono già ordinati:

join file1 file2 

non se:

join <(sort file1) <(sort file2) 
4

Se si utilizza perl versione 5.10 o più recente, è possibile unire i termini 'query' in un'espressione regolare con i termini della query separati dal 'pipe'. (Come: ENST001|ENST002|ENST003) Perl costruisce un 'trie' che, come un hash, effettua ricerche in tempo costante. Dovrebbe essere veloce quanto la soluzione utilizzando un hash di ricerca. Solo per mostrare un altro modo per farlo.

#!/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