如何在shell中合并具有不同行数的两个文件?

Dad*_*ang 7 shell terminal join bioinformatics

我有这样的file1:

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
Run Code Online (Sandbox Code Playgroud)

和 file2 像:

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
Run Code Online (Sandbox Code Playgroud)

我想根据 SNP 名称和 TEST==ALL 将 file2 与 file1 合并,并在输出 file3 中保留 CHR、SNP、P 和 MAF。有人知道在终端(Unix)shell 中最好的方法吗?

所需的输出是:

  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
Run Code Online (Sandbox Code Playgroud)

JPG*_*JPG 6

这个答案的帮助下

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

要获得标题,只需将其添加到脚本的开头:

 BEGIN{print "CHR SNP MAF P"}
Run Code Online (Sandbox Code Playgroud)

解释:

首先,当两个文件传递给awk时,它们是一个接一个地处理的。这里有两个重要的变量:NRawk命令开头FNR的行号,以及当前文件开头的行号。即当第一个文件被处理时(这里是file2),NR并且FNR有相同的值,也就是当前处理的行的值。但是当 awk 传递给第二个文件时,它FNR被重置为 1,因此NRFNR不再相同。因此,测试FNR==NR是了解处理的文件是否是第一个的技巧。

那么让我们看看代码。条件FNR==NR && FNR>1测试我们是否正在处理第一个文件而不是第一行。如果是这种情况,我们将第五列 ( MAF)的值存储在由第二列 ( ) 索引的数组中SNP,然后next语句说要传递到下一行。

当awk处理第二个文件(也就是file1)时,第一个测试为false,这样awk就尝试第二个测试:FNR > 1 && ($2 in a) && $3 == "ALL",即:不是文件的第一行+第二列值(SNP)存在于表中a+第三列值( TEST) 是"ALL"。如果是,则打印第 1 ( CHR)列和第 2 ( SNP) 列,使用 获取MAF数组中的值a[$2],然后打印第 9 列 ( P)。

BEGIN{...}在开头添加一个语句会添加一个仅在处理第一行之前运行的命令。


gre*_*eke 5

terdon提到只用 coreutils 做这件事,一个谦虚的建议:

(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
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)