Filtra o arquivo quando uma coluna está na lista de permissões

2

Suponha que eu tenha dois arquivos, Arquivo A:

a,abcdef
b,bcdefa
c,cdefab
a,defabc
b,efabcd
c,fabcde

E o arquivo B:

a
b

A saída que estou procurando é:

a,abcdef
b,bcdefa
a,defabc
b,efabcd

Então, basicamente, eu quero selecionar as linhas do Arquivo A onde a primeira coluna corresponde a qualquer valor no Arquivo B usando comandos unix padrão. Uma espécie de awk {if (file_b contains $1} print $1,$2 , mas mais eficiente.

O número esperado de linhas no Arquivo A excede 20 milhões e o Arquivo B 1 milhão. Ele deve ser executado em O (n), portanto, a etapa contém deve depender de uma tabela de hash.

    
por Hugo Sereno Ferreira 02.06.2013 / 14:14

3 respostas

3

Existem muitas maneiras de fazer isso. Aqui estão alguns, mas use o Perl é ordens de magnitude mais rápido. Eu incluo os outros para completar:

  1. Perl e hashes, ridiculamente rápidos

    perl -e 'open(A,"fileB"); while(<A>){chomp; $k{$_}++} 
     while(<>){@a=split(/,/); print if defined $k{$a[0]}}' fileA
    
  2. Gawk e matrizes associativas, muito mais lentas

     gawk '{if(FILENAME==ARGV[1]){array[$1]=1}
      else{if($1 in array){print}}}' fileA fileB
    
  3. grep , ridiculamente lento. Você precisará modificar seu arquivo B levemente para fazer com que os padrões correspondam apenas na primeira linha

    sed 's/\(.\)/^/' fileB > fileC
    grep -f fileC fileA  
    

Eu criei alguns arquivos de teste e as soluções Perl são muito mais rápidas que o grep :

$ head -2 fileA
GO:0032513_GO:0050129
GO:0050129_GO:0051712
$ head -2 fileB
GO:0032513_GO:0050129
GO:0050129_GO:0051712
$ wc -l fileA fileB
  1500000 fileA
 20000000 fileB
$ time perl -e 'open(A,"fileB"); while(<A>){chomp; $k{$_}++} 
 while(<>){@a=split(/,/); print if defined $k{$a[0]}}' fileA > /dev/null 

real    0m41.354s
user    0m37.370s
sys     0m3.960s
$ time gawk '{if(FILENAME==ARGV[1]){array[$1]=1}
   else{if($1 in array){print}}}' fileA fileB

real    2m30.963s
user    1m23.857s
sys     0m9.385s
$ time (join -t, <(sort -n fileA) <(sort -n fileB) >/dev/null)

real    8m29.532s
user    13m52.576s
sys     1m22.029s

Assim, o scriptlet Perl pode passar por um arquivo de 20 milhões de linhas, procurando por 1,5 milhão de padrões e terminar em ~ 40 segundos. Não é ruim. Os outros dois são muito mais lentos, gawk demorou 2,5 minutos e o grep foi executado por mais de 15. Perl vence com facilidade.

    
por 02.06.2013 / 16:13
2

Isso deve funcionar:

join -t, <(sort A) <(sort B)
    
por 02.06.2013 / 16:29
0

eu ainda não entendo o que você está tentando fazer .... você já tem algo em um programa awk.

O

grep é básico e é baseado em um algoritmo rápido e eficiente, baseado no índice de possíveis correspondências .... o awk faz uma comparação individual e verifica se isso deve ser rápido .... wrt awk, aqui

 for pat in 'cat zfile2' ; do  grep -i "$pat," zfile1 ; done; 

isso funciona bem ...

   $ for pat in 'cat zfile2' ; do  grep -i "$pat," zfile1 ; done;
   a,abcdef
   a,defabc
   b,bcdefa
   b,efabcd

isso ajuda?

    
por 02.06.2013 / 16:13