Como mesclar dois arquivos com base na correspondência de duas colunas?

33

Tenho file1 gosta:

0   AFFX-SNP-000541  NA
0   AFFX-SNP-002255  NA
1   rs12103          0.6401
1   rs12103_1247494  0.696
1   rs12142199       0.7672

E um arquivo2:

0   AFFX-SNP-000541   1
0   AFFX-SNP-002255   1
1   rs12103           0.5596
1   rs12103_1247494   0.5581
1   rs12142199        0.4931

E gostaria de um arquivo3 que:

0   AFFX-SNP-000541     NA       1
0   AFFX-SNP-002255     NA       1
1   rs12103             0.6401   0.5596
1   rs12103_1247494     0.696    0.5581
1   rs12142199          0.7672   0.4931

O que significa colocar a quarta coluna do arquivo2 em arquivo1 pelo nome da 2ª coluna.

Dadong Zhang
fonte
1
Arquivo2 só tem três colunas?
Bernhard

Respostas:

48

Isso deve servir:

join -j 2 -o 1.1,1.2,1.3,2.3 file1 file2

Importante : isso pressupõe que seus arquivos sejam classificados (como no seu exemplo) de acordo com o nome do SNP. Se não estiverem, classifique-os primeiro:

join -j 2 -o 1.1,1.2,1.3,2.3 <(sort -k2 file1) <(sort -k2 file2)

Saída:

0 AFFX-SNP-000541 NA 1
0 AFFX-SNP-002255 NA 1
1 rs12103 0.6401 0.5596
1 rs12103_1247494 0.696 0.5581
1 rs12142199 0.7672 0.4931

Explicação (de info join):

`join 'grava na saída padrão uma linha para cada par de linhas de entrada que possuem campos de junção idênticos.

`-1 FIELD'
     Join on field FIELD (a positive integer) of file 1.

`-2 FIELD'
     Join on field FIELD (a positive integer) of file 2.

`-j FIELD'
     Equivalent to `-1 FIELD -2 FIELD'.

`-o FIELD-LIST'

 Otherwise, construct each output line according to the format in
 FIELD-LIST.  Each element in FIELD-LIST is either the single
 character `0' or has the form M.N where the file number, M, is `1'
 or `2' and N is a positive field number.

Portanto, o comando acima une os arquivos no segundo campo e imprime o primeiro, o segundo e o terceiro campos do arquivo um, seguidos pelo terceiro campo do arquivo2.

terdon
fonte
16

Você poderia usar awk:

$ awk 'NR==FNR {h[$2] = $3; next} {print $1,$2,$3,h[$2]}' file2 file1 > file3

saída:

$ cat file3
0 AFFX-SNP-000541 NA 1
0 AFFX-SNP-002255 NA 1
1 rs12103 0.6401 0.5596
1 rs12103_1247494 0.696 0.5581
1 rs12142199 0.7672 0.4931

Explicação:

Percorrer file2( NR==FNRé verdade apenas para o primeiro argumento de arquivo). Guardar coluna 3 na mistura-matriz usando coluna 2 como chave: h[$2] = $3. Em seguida, percorra file1e produza todas as três colunas $1,$2,$3, anexando a coluna salva correspondente do hash-array h[$2].

Grebneke
fonte
Muito obrigado. Apenas imaginando, o que significa 'h [$ 2] = $ 3'? Na verdade, preciso corresponder exatamente ao arquivo1 $ 2 == arquivo2 $ 2 nos meus casos complexos (que não são necessários na mesma ordem).
Dadong Zhang
1
h[$2] = $3é uma atribuição de hash. Salve $3como valor e $2como chave. Exemplo: h["name"] = "Dadong". Agora, print h["name"]saídas Dadong. Ele faz o que você deseja, corresponde exatamente à segunda coluna dos dois arquivos.
grebneke
6

Se você não precisar de nenhum pedido, uma solução simples seria

paste file{1,2} | awk '{print $1,$2,$3,$6}' > file3

Isso pressupõe que todas as linhas têm três entradas e as colunas 1 e 2 dos dois arquivos são iguais (como nos dados de exemplo)

Bernhard
fonte
1
+1 para um ótimo uso de #paste
grebneke
1
@grebneke e Bernhard, já que você parece ser fã, pasteconsegue descobrir uma maneira de responder isso com coreutils?
terdon
@terdon - uma humilde tentativa: unix.stackexchange.com/a/113909/32165
grebneke
1
@terdon eu aconselho a reconsiderar o programa que está a emitir este s ***
Bernhard
Nada de errado com o formato, arquivos separados por guias perfeitamente decentes. De qualquer forma, com esse tipo de dados você geralmente não tem escolha quanto ao formato, ele sai de outro programa.
terdon