Làm cách nào để hợp nhất hai tệp với số lượng hàng khác nhau trong shell?


7

Tôi có file1 như thế này:

CHR                     SNP     TEST   A1   A2                 GENO   O(HET)   E(HET)            P 
   0         AFFX-SNP-000541      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-000541      AFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-000541    UNAFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-002255      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-002255      AFF    0    0                0/0/0      nan      nan           NA
   0         AFFX-SNP-002255    UNAFF    0    0                0/0/0      nan      nan           NA
   1                 rs12103      ALL    C    T           55/250/317   0.4019   0.4113       0.5596
   1                 rs12103      AFF    C    T                0/0/0      nan      nan           NA
   1                 rs12103    UNAFF    C    T                0/0/0      nan      nan           NA
   1         rs12103_1247494      ALL    C    T           55/250/321   0.3994   0.4097       0.5581
   1         rs12103_1247494      AFF    C    T                0/0/0      nan      nan           NA
   1         rs12103_1247494    UNAFF    C    T                0/0/0      nan      nan           NA

Và file2 như:

CHR                     SNP   A1   A2          MAF  NCHROBS
   0         AFFX-SNP-000541    0    0           NA        0
   0         AFFX-SNP-002255    0    0           NA        0
   1                 rs12103    C    T       0.2894     1244
   1         rs12103_1247494    C    T       0.2875     1252

Tôi muốn hợp nhất file2 với file1 dựa trên tên SNP và TEST == ALL và giữ CHR, SNP, P và MAF trong tệp đầu ra3. Có ai biết cách tốt nhất cho shell đó (Unix) không?

Một đầu ra mong muốn sẽ là:

  CHR                     SNP  MAF        P
   0         AFFX-SNP-000541   NA         1
   0         AFFX-SNP-002255   NA         1
   1                 rs12103   0.2894     0.5596
   1         rs12103_1247494   0.2875     0.5581

Vui lòng cho một ví dụ về đầu ra mong muốn của bạn.
terdon

Làm thế nào lớn là các tập tin? Có thể ở cuối tập tin1 lại có một dòng với 'AFFX-SNP-000541' cho trường SNP không?
brm

Số 'AFFX-SNP-000541' của tệp2 hoàn toàn khớp với 'AFFX-SNP-000541' + 'ALL' của tệp1.
Dadong Zhang

Ngay cả khi có thể làm điều này với các tệp văn bản: đôi khi việc nhập dữ liệu vào cơ sở dữ liệu quan hệ có hợp lý không?
Simon Richter

Câu trả lời:


6

Với sự giúp đỡ từ câu trả lời này

awk 'FNR==NR && FNR>1 {a[$2] = $5; next}
     FNR > 1 && ($2 in a) && $3 == "ALL" {
         print $1 "    " $2 "    "  a[$2] "    "  $9
     }' file2 file1

Để có được tiêu đề là tốt, chỉ cần thêm phần này vào phần đầu của tập lệnh:

 BEGIN{print "CHR SNP MAF P"}

Giải trình:

Trước hết, khi hai tệp được chuyển đến awk, chúng được xử lý lần lượt. Có hai biến quan trọng ở đây: NRlà số dòng từ đầu awklệnh và FNRlà số dòng từ đầu tệp hiện tại. Đó là, khi tệp đầu tiên được xử lý (ở đây là tệp 2) NRFNRcó cùng giá trị, đó là giá trị của dòng hiện đang được xử lý. Nhưng khi awk chuyển sang tập tin thứ hai, FNRđược đặt lại thành 1, do đó NRFNRkhông còn giống nhau. Vì vậy, bài kiểm tra FNR==NRlà một mẹo để biết liệu tập tin được xử lý có phải là lần đầu tiên hay không.

Vì vậy, hãy xem mã. Các điều kiện FNR==NR && FNR>1kiểm tra xem chúng tôi đang xử lý tệp đầu tiên chứ không phải dòng đầu tiên. Nếu đúng như vậy, chúng ta lưu trữ giá trị của cột thứ năm ( MAF) trong một mảng được lập chỉ mục bởi cột thứ hai ( SNP) và sau đó nextcâu lệnh nói sẽ chuyển sang dòng sau.

Khi awk xử lý tệp thứ hai (là tệp 1), kiểm tra đầu tiên là sai, do đó awk thử kiểm tra thứ hai : FNR > 1 && ($2 in a) && $3 == "ALL", đó là: không phải là dòng đầu tiên của tệp + giá trị cột thứ hai ( SNP) tồn tại trong bảng a+ giá trị cột thứ ba ( TEST) là "ALL". Nếu đó là trường hợp, sau đó nó in cột 1 ( CHR) và hai ( SNP), nhận MAFgiá trị từ mảng với a[$2], và sau đó in cột chín ( P).

Thêm một BEGIN{...}câu lệnh ở đầu sẽ thêm một lệnh chỉ chạy trước khi dòng đầu tiên được xử lý.


Vâng, nếu tiêu đề được biết đến, người ta có thể thêm BEGIN{print "CHR SNP MAF P"}vào lúc đầu.
JPG

Bạn có thể đưa ra một lời giải thích? Cảm ơn công việc tuyệt vời của bạn!
Dadong Zhang

5

terdon đề cập đến việc làm nó chỉ với coreutils, một gợi ý khiêm tốn:

(echo CHR SNP MAF P
 paste <(tail -n +2 file2) <(grep ALL file1) | 
   while read -r chr snp _ _ maf _ _ _ _ _ _ _ _ _ p; do 
     echo $chr $snp $maf $p; 
   done) | column -t

đầu ra:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
0    AFFX-SNP-002255  NA      1
1    rs12103          0.2894  0.5596
1    rs12103_1247494  0.2875  0.5581

+1 b / c dán là công cụ Unix yêu thích của tôi. Nó thậm chí có thể cung cấp cho bạn hòa bình thế giới nếu bạn để nó 8-)
slm

@slm - "b / c", cái gì vậy?
grebneke

@grebneke - b / c = vì
slm

1
@slm - cảm ơn, đã sắp xếp rồi. Chỉ có hòa bình thế giới để đi và chúng ta đã hoàn thành.
grebneke

4

Đây là một cách để làm điều đó (sao chép / dán trực tiếp vào thiết bị đầu cuối của bạn):

(
 awk -v OFS="\t" 'NR==1{print $1,$2,$5,"P"}' file2;
 awk '$3=="ALL"{print $2,$NF}' file1 | 
  while read snp p; do 
   awk -v snp="$snp" -v p=$p -v OFS="\t" '$2==snp{print $1,$2,$5,p}' file2;
  done
) > file3

Đầu ra trông như thế này:

CHR SNP MAF P
0   AFFX-SNP-000541 0   1
0   AFFX-SNP-002255 0   1
1   rs12103 C   0.5596
1   rs12103_1247494 C   0.5581

Ghi chú:

  • Tôi 99% chắc chắn bạn có thể làm điều này bằng cách nào đó sử dụng coreutils, có lẽ là một sự kết hợp của sort, pastejoinnhưng tôi không thể hình dung nó ra.
  • Điều này không hiệu quả và có thể mất một thời gian nếu bạn đang xử lý các tệp lớn.
  • Điều này thay đổi các dấu tách trường thành các tab, loại bỏ tất cả các trường hợp -v OFS="\t"nếu bạn không muốn điều đó.
  • Như bạn có thể thấy, các trường không còn được căn chỉnh. Vì tất cả chúng được phân tách bằng các tab, đây không phải là vấn đề đối với bất kỳ chương trình nào sẽ đọc tệp, nó chỉ là vấn đề nếu bạn cố đọc nó.

Giải trình

  • Dòng thứ 1 awkchỉ in trường 1, 2 và 5 của tiêu đề của tệp 2 theo sau P. Đây là tiêu đề của tập tin mới.
  • Cái thứ hai awkin các trường thứ 2 và cuối ( $NF) của mỗi dòng của tệp1 trong đó trường thứ 3 là ALL.
  • Điều này sau đó được chuyển qua một whilevòng lặp shell để đọc hai trường vào các biến $snp$p.
  • Sau đó, chúng được đưa cho tùy chọn cuối cùng awkbằng cách sử dụng -vtùy chọn và sau đó, đối với mỗi dòng của tệp 2, nếu trường thứ hai là $snp, nó sẽ in các trường 1,2,5 và giá trị hiện tại của $p.

Đây là một kịch bản perl làm tương tự:

perl -le 'print "CHR\tSNP\tMAF\tP"; open($f1, "$ARGV[0]"); 
          while(<$f1>){
           s/^\s+//; @f=split(/\s+/); $k{$f[1]}=$f[$#f] if $f[2] eq "ALL"
          } 
          open($f2,"$ARGV[1]"); 
          while(<$f2>){
            s/^\s+//; @f=split(/\s+/); 
            next unless defined($k{$f[1]}); 
            print "$f[0]\t$f[1]\t$f[4]\t$k{$f[1]}"
          }' file1 file2

1

Đây là một cách khác để làm điều đó:

{ printf %s\\n "CHR SNP MAF P"; grep -F ALL file1 | sort -k2,2 | \
join -j2 -o 2.1,2.2,2.5,1.9 - <(sort -k2,2 file2); } | column -t

đầu ra:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
0    AFFX-SNP-002255  NA      1
1    rs12103          0.2894  0.5596
1    rs12103_1247494  0.2875  0.5581

Làm thế nào nó hoạt động:

grep -F ALL file1 | sort -k2in các dòng khớp với chuỗi ALL, sau đó là sorted trên trường thứ 2:

   0         AFFX-SNP-000541      ALL    0    0                0/0/0      nan      nan            1
   0         AFFX-SNP-002255      ALL    0    0                0/0/0      nan      nan            1
   1         rs12103_1247494      ALL    C    T           55/250/321   0.3994   0.4097       0.5581
   1                 rs12103      ALL    C    T           55/250/317   0.4019   0.4113       0.5596

sau đó nó được nối trên trường thứ 2 ( -j2) với tệp 2 (cũng được sắp xếp trên trường thứ 2) bằng cách sử dụng -o 2.1,2.2,2.5,1.9để xuất trường thứ 1, 2 và 5 từ tệp 2 và trường thứ 9 từ tệp được xử lý1:

0 AFFX-SNP-000541 NA 1
0 AFFX-SNP-002255 NA 1
1 rs12103 0.2894 0.5596
1 rs12103_1247494 0.2875 0.5581

Vì điều này đã được nhóm {...}với printf %s\\n "CHR SNP MAF P"in tiêu đề, toàn bộ đầu ra sau đó được thêm vào column -t.
Lưu ý rằng với giải pháp này, thứ tự dòng (từ tệp2) không được giữ nguyên trong đầu ra. Nó chỉ xảy ra rằng file2 của bạn đã được sắp xếp trên trường thứ 2 nhưng nếu nó không phải là:

CHR                     SNP   A1   A2          MAF  NCHROBS
   0         AFFX-SNP-000541    0    0           NA        0
   1                 rs12103    C    T       0.2894     1244
   0         AFFX-SNP-002255    0    0           NA        0
   1         rs12103_1247494    C    T       0.2875     1252

và nếu bạn muốn giữ nguyên thứ tự dòng, bạn có thể đánh số các dòng trong tệp2 bằng nl -ba -nrz file2:

000001  CHR                     SNP   A1   A2          MAF  NCHROBS
000002     0         AFFX-SNP-000541    0    0           NA        0
000003     1                 rs12103    C    T       0.2894     1244
000004     0         AFFX-SNP-002255    0    0           NA        0
000005     1         rs12103_1247494    C    T       0.2875     1252

trước khi sắp xếp và nối và điều chỉnh lệnh thành: tham gia vào trường thứ 2 của tệp 1 (đã xử lý) và trường thứ 3 của tệp 2 đã xử lý và xuất ra trường số dòng + các trường giống như giải pháp đầu tiên, join -1 2 -2 3 -o 2.1,2.2,2.3,2.6,1.9sau đó sắp xếp lại đầu ra với sort -k1nvà xóa trường đầu tiên với cut -d' ' -f2-trước khi đặt toàn bộ đầu ra tới column -t:

{ printf %s\\n "CHR SNP MAF P"; grep -F ALL file1 | sort -k2,2 | join \
-1 2 -2 3 -o 2.1,2.2,2.3,2.6,1.9 - <(sort -k3,3 <(nl -ba -nrz file2)) | \
sort -k1n | cut -d' ' -f2-; } | column -t

đầu ra:

CHR  SNP              MAF     P
0    AFFX-SNP-000541  NA      1
1    rs12103          0.2894  0.5596
0    AFFX-SNP-002255  NA      1
1    rs12103_1247494  0.2875  0.5581

Lưu ý rằng cả hai giải pháp đều giả sử chỉ có một lần xuất hiện ALLtrên mỗi dòng, cụ thể là trường thứ 3 (do đó là grep -F ALL). Nếu đó không phải là trường hợp, bạn sẽ phải sử dụng biểu thức chính quy để chỉ lọc các dòng có liên quan.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.