Jak scalić dwa pliki na podstawie dopasowania dwóch kolumn?

33

Mam plik Like 1:

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

I plik2:

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

I chciałby plik3 taki, że:

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

Co oznacza wstawienie czwartej kolumny pliku2 do pliku1 pod nazwą drugiej kolumny.

Dadong Zhang
źródło
1
File2 ma tylko trzy kolumny?
Bernhard

Odpowiedzi:

48

To powinno to zrobić:

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

Ważne : zakłada się, że twoje pliki są posortowane (jak w twoim przykładzie) według nazwy SNP. Jeśli nie, posortuj je najpierw:

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

Wydajność:

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

Objaśnienie (z info join):

`join 'wypisuje na standardowe wyjście linię dla każdej pary linii wejściowych, które mają identyczne pola łączenia.

`-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.

Tak więc powyższe polecenie łączy pliki w drugim polu i drukuje 1., 2. i 3. pole pliku 1, a następnie 3. pole pliku2.

terdon
źródło
16

Możesz użyć awk:

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

wydajność:

$ 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

Wyjaśnienie:

Przejście file2( NR==FNRdotyczy tylko pierwszego argumentu pliku). Zapisz w kolumnie 3 tablicy używając hash-2 jako kolumny klucza: h[$2] = $3. Następnie przejdźfile1 i wyślij wszystkie trzy kolumny $1,$2,$3, dołączając odpowiednią zapisaną kolumnę z tablicy mieszającej h[$2].

grebneke
źródło
Wielkie dzięki. Zastanawiam się, co oznacza „h [2 $] = 3 $”? Właściwie muszę dokładnie dopasować plik1 $ 2 == plik2 $ 2 w moich skomplikowanych przypadkach (które nie są konieczne w tej samej kolejności).
Dadong Zhang
1
h[$2] = $3to zadanie mieszania. Zapisuje $3jako wartość i $2jako klucz. Przykład: h["name"] = "Dadong". Teraz print h["name"]wyjścia Dadong. Robi to, co chcesz, dokładnie pasuje do drugiej kolumny z obu plików.
grebneke
6

Jeśli nie potrzebujesz żadnego zamówienia, byłoby proste rozwiązanie

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

Zakłada się, że wszystkie wiersze mają trzy wpisy, a kolumny 1 i 2 obu plików są takie same (jak w przykładowych danych)

Bernhard
źródło
1
+1 za świetne wykorzystaniepaste
grebneke
1
@grebneke i Bernhard, skoro wydajesz się być fanem, pasteczy możesz znaleźć sposób, aby odpowiedzieć na to z Coreutils?
terdon
@terdon - pokorna próba: unix.stackexchange.com/a/113909/32165
grebneke
1
@terdon Radzę ponownie rozważyć program, który wypisuje to ***
Bernhard
Nie ma nic złego w formacie, idealnie przyzwoite pliki rozdzielone tabulatorami. W każdym razie przy tego rodzaju danych zwykle nie masz wyboru co do formatu, pochodzi on z innego programu.
terdon