¿Cómo fusionar dos archivos en función de la coincidencia de dos columnas?

33

Tengo file1 me gusta:

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

Y un archivo2:

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

Y quisiera un archivo3 tal 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

Lo que significa poner la cuarta columna del archivo2 en el archivo1 por el nombre de la segunda columna.

Dadong Zhang
fuente
1
File2 solo tiene tres columnas?
Bernhard

Respuestas:

48

Esto debería hacerlo:

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

Importante : esto supone que sus archivos están ordenados (como en su ejemplo) de acuerdo con el nombre de SNP. Si no lo están, ordénelos primero:

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

Salida:

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

Explicación (de info join):

'join' escribe en la salida estándar una línea para cada par de líneas de entrada que tienen campos de unión 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.

Entonces, el comando anterior une los archivos en el segundo campo e imprime el primer, segundo y tercer campo del archivo uno, seguido del tercer campo del archivo2.

terdon
fuente
16

Podrías usar awk:

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

salida:

$ 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

Explicación:

Recorrer file2( NR==FNRsolo es cierto para el primer argumento de archivo). Guardar en la columna 3 de hash-array usando la columna 2 como la clave: h[$2] = $3. Luego recorra file1y genere las tres columnas $1,$2,$3, agregando la columna guardada correspondiente de la matriz hash h[$2].

grebneke
fuente
Muchas gracias. Solo me pregunto, ¿qué significa 'h [$ 2] = $ 3'? En realidad, necesito hacer coincidir exactamente el archivo1 $ 2 == archivo2 $ 2 en mis casos complejos (que no son necesarios en el mismo orden).
Dadong Zhang
1
h[$2] = $3es una tarea hash. Se guarda $3como el valor y $2como la clave. Ejemplo: h["name"] = "Dadong". Ahora, print h["name"]salidas Dadong. Hace lo que quiere, coincide exactamente con la segunda columna de ambos archivos.
grebneke
6

Si no necesita ningún pedido, una solución simple sería

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

Esto supone que todas las filas tienen tres entradas, y las columnas 1 y 2 de ambos archivos son iguales (como en los datos de ejemplo)

Bernhard
fuente
1
+1 para un gran uso depaste
grebneke
1
@grebneke y Bernhard, ya que parece que eres fanático de paste¿puedes encontrar una manera de responder esto con coreutils?
terdon
@terdon - un intento humilde: unix.stackexchange.com/a/113909/32165
grebneke
1
@terdon Aconsejaría que reconsiderara el programa que está produciendo este s ***
Bernhard
No hay nada malo con el formato, archivos separados por tabulaciones perfectamente decentes. En cualquier caso, con este tipo de datos, generalmente no tiene otra opción en cuanto al formato, proviene de otro programa.
terdon