como excluir algumas das linhas no primeiro arquivo com base nas informações de um segundo arquivo?

3

Eu tenho um arquivo como este:

arquivo 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

e arquivo 2:

1
3 
4
5 6 7

Eu quero manter essas linhas no arquivo 1, no qual os números dos pares são observados juntos em uma linha no arquivo2. aqui apenas 5, 6 e 7 são observados juntos no arquivo 2. portanto, cada par desses números deve permanecer no arquivo1. Então, a saída é:

5   6       0.995
5   7       0.658
6   7       0.431

alguma sugestão (considerando que os dados reais são enormes e os números podem não começar de 1)?

    
por zara 06.10.2016 / 07:31

2 respostas

3

Se perl solução estiver bem. Presume que os dados column1 e column2 no arquivo1 sejam ordenados de modo que o valor na coluna1 seja sempre menor que column2

$ 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
  • Primeiro, um hash de chaves é construído no arquivo2 em pares (assumindo que os números estão organizados em ordem crescente)
    • os valores das duas colunas são cercados por - para evitar possíveis incompatibilidades, como 11 e 20 vs 1 e 120
  • Em seguida, para as linhas file1, imprima a linha se a chave existir


Se file2 for alterado como

$ 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
    
por 06.10.2016 / 09:54
3

Usando o awk.

O primeiro comando do awk cria um arquivo com todos os pares. O segundo comando awk lê o arquivo de pares uma vez em uma matriz e, em seguida, imprime todas as linhas correspondentes.

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

O segundo comando pode precisar de muita memória! Se os arquivos forem ordenados, poderemos evitar o array de alguma forma, lendo os dois arquivos simultaneamente. Note que estou usando dois comandos para poder revisar o arquivo de pares antes da execução final.

Aqui, o mesmo código de um único comando e legível formatado:

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

FYI Eu executei alguns benchmarks em file1 (22 mill. linhas), file2 (4 mill lines) que produz 2 mill. linhas de saída.

  • gawk: 9,6 segundos, 275 milhões de memória
  • mawk: 20,7 segundos, 134 milhões de memória
  • Resposta perlita de Sundeep: 31,9 seg., 231M de memória
por 06.10.2016 / 09:33