Comparando dois arquivos usando Unix e Awk

3

Eu preciso comparar dois arquivos, Arquivo1 e Arquivo2 (separados por espaço) usando 4 campos (Campo 1, 2, 4 e 5 do Arquivo 1 com campo1, 2, 4 e 5 do Arquivo2).

Lógica:
Se as colunas 1, 2 e 4 do Arquivo 1 corresponderem à coluna 1, 2 e 4 do Arquivo 2 e houver uma incompatibilidade na coluna 5, ambas as linhas do Arquivo 1 e Arquivo 2 serão concatenadas redirecionadas como saída. Portanto, o arquivo de saída contém apenas as linhas onde a coluna 1, 2 e 4 do arquivo1 e arquivo2 corresponde e coluna 5 não corresponde.

Arquivo1:

sc2/80         20      .        A       T         86       PASS     N=2     F=5;U=4
sc2/60         55      .        G       T         76       PASS     N=2     F=5;U=4 
sc2/68         20      .        T       C         71       PASS     N=2     F=5;U=4
sc2/24         24      .        T       G         31       PASS     N=2     F=5;U=4

Arquivo2:

sc2/80         20      .        A        C        80      PASS    N=2       F=5;U=4
sc2/60         55      .        G        C        72      PASS    N=2       F=5;U=4 
sc2/68         20      .        T        C        71      PASS    N=2       F=5;U=4 
sc2/10         24      .        T        G        31      PASS    N=2       F=5;U=4
sc2/40         59      .        T        G        31      PASS    N=2       F=5;U=4
sc2/24         24      .        A        G        38      PASS    N=2       F=5;U=4

Saída:

sc2/80         20      .        A       T        86      PASS     N=2      F=5;U=4
sc2/80         20      .        A       C        80      PASS     N=2      F=5;U=4

sc2/60         55      .        G       T        76      PASS     N=2      F=5;U=4 
sc2/60         55      .        G       C        72      PASS     N=2      F=5;U=4

Sou novo no ramo e agradeço sua ajuda.

    
por Namrata 29.07.2013 / 01:45

2 respostas

8

Você pode usar awk . Coloque o seguinte em um script, script.awk :

FNR == NR {
  f1[$1,$2,$4] = $0
  f1_c14[$1,$2,$4] = 1
  f1_c5[$1,$2,$4] = $5
  next
}  

f1_c14[$1,$2,$4] {
  if ($5 != f1_c5[$1,$2,$4]) print f1[$1,$2,$4];
}

f1[$1,$2,$4] {
  if ($5 != f1_c5[$1,$2,$4]) print $0;
}

Agora execute assim:

$ awk -f script.awk file1  file2
sc2/80         20      .        A       T         86       PASS     N=2     F=5;U=4
sc2/80         20      .        A        C        80      PASS    N=2       F=5;U=4
sc2/60         55      .        G       T         76       PASS     N=2     F=5;U=4 
sc2/60         55      .        G        C        72      PASS    N=2       F=5;U=4

O script funciona da seguinte maneira. Este bloco cria três matrizes, f1 , f1_c14 e f1_c5 . f1 contém todas as linhas do arquivo1 em uma matriz, indexadas usando o conteúdo das colunas 1, 2 e & 4 do arquivo1. f1_c14 é outro array com o mesmo índice (conteúdo de 1, 2 e 4) e um valor de 1 . O terceiro array usa o mesmo índice do primeiro 2, com o valor da quinta coluna do arquivo1.

FNR == NR {
  f1[$1,$2,$4] = $0
  f1_c14[$1,$2,$4] = 1
  f1_c5[$1,$2,$4] = $5
  next
} 

O próximo bloco é responsável por imprimir linhas do primeiro arquivo, file1 sob as condições que as colunas 1, 2 e & 4 corresponde as colunas de file2 , AND e imprimirá a linha de file1 se as 5 colunas de file1 e file2 não corresponderem.

f1_c14[$1,$2,$4] {
  if ($5 != f1_c5[$1,$2,$4]) print f1[$1,$2,$4];
}

O terceiro bloco é responsável por imprimir a linha associada de file2 , há uma linha correspondente na matriz f1 para as colunas 1, 2, & file2 4. Novamente, ele só imprime se as 5a colunas não corresponderem.

f1[$1,$2,$4] {
  if ($5 != f1_c5[$1,$2,$4]) print $0;
}

Exemplo

Executando o script acima da seguinte forma:

$ awk -f script.awk file1  file2
sc2/80         20      .        A       T         86       PASS     N=2     F=5;U=4
sc2/80         20      .        A        C        80      PASS    N=2       F=5;U=4
sc2/60         55      .        G       T         76       PASS     N=2     F=5;U=4 
sc2/60         55      .        G        C        72      PASS    N=2       F=5;U=4 

Você pode usar o comando column para limpar um pouco a saída:

$ awk -f script.awk file1  file2 | column -t
sc2/80  20  .  A  T  86  PASS  N=2  F=5;U=4
sc2/80  20  .  A  C  80  PASS  N=2  F=5;U=4
sc2/60  55  .  G  T  76  PASS  N=2  F=5;U=4
sc2/60  55  .  G  C  72  PASS  N=2  F=5;U=4

Como funciona?

FNR == NR

Isso faz uso da capacidade de awk de fazer um loop pelos arquivos de uma maneira específica. Aqui estamos nós percorrendo os arquivos e quando estamos em uma linha do primeiro arquivo, file , queremos executar um determinado bloco de código nesta linha a partir de file1 .

Este exemplo mostra o que o FNR == NR está fazendo quando damos 2 arquivos simulados. Um tem 4 linhas enquanto o outro tem 5 linhas:

$ awk 'BEGIN {print "NR\tFNR\tline"} {print NR"\t"FNR"\t"$0}' \
     <(seq 1 4) <(seq 1 5)
NR  FNR line
1   1   1
2   2   2
3   3   3
4   4   4
5   1   1
6   2   2
7   3   3
8   4   4
9   5   5
outros blocos

Os outros blocos, f1_c14[$1,$2,$4] AND f1[$1,$2,$4] , só são executados quando os valores desses elementos da matriz têm um valor.

    
por 29.07.2013 / 01:57
1

Aqui está uma solução em Perl. Você deve salvar o seguinte código em um arquivo e executá-lo como um script (veja abaixo):

#!/usr/bin/perl
$file1 = '/path/to/file1';
$file2 = '/path/to/file2';
open $f1,'<',$file1;
open $f2,'<',$file2;
while(<$f1>){
    ($c1,$c2,$c4,$c5) = (split / /)[0,1,3,4]; #get relevant columns in file 1
    $lines_dictionary{"$c1 $c2 $c4"}="$c5---$_"; #create a hash entry keyed by the relevant columns
}
while(<$f2>){
    ($c1,$c2,$c4,$c5) = (split / /)[0,1,3,4]; #get relevant columns in file 2
    if(exists $lines_dictionary{"$c1 $c2 $c4"}){ #if a line with similar columns was seen in file 1
        ($file1_c5,$file1_line) = split /---/,$lines_dictionary{"$c1 $c2 $c4"}; #parse the hash entry this line in file 1
        if($file1_c5 -ne $c5){ #if column 5 of file 2 doesn't match column 5 of file 1
            print "${file1_line}$_\n"; #we only need one extra newline as the lines read from the files have trailing ones.
        }
    }
}
close $f1;
close $f2;

Use qualquer editor de texto para colar esse script em um arquivo, modifique as variáveis $file1 e $file2 para refletir os locais verdadeiros de seus arquivos e, em seguida, torne-o executável:

$ chmod +x /path/to/script

Por fim, chame o script:

$ /path/to/script

Aviso de isenção

  • Este código não foi testado
  • Este código assume que o padrão '---' é improvável de ocorrer na quinta coluna.
  • Este código assume que as linhas no arquivo 1 são exclusivas (ou seja, cada linha tem uma combinação diferente de "coluna1 coluna2 coluna4"). Se houver várias linhas (não necessariamente consecutivas) contendo os mesmos dados nas colunas relevantes, o script usará o último (mais abaixo no arquivo) dessas linhas.
por 29.07.2013 / 02:23

Tags