如何使用 awk 比较两个文件的两列并打印不匹配的模式

如何使用 awk 比较两个文件的两列并打印不匹配的模式

我有一个数据文件A.tsv(字段分隔符= \t):

id  clade   mutation
243 40A siti,toto,mumu
254     
267 40B lala,sisi,sojo

和一个模板文件B.tsv(字段分隔符 = \t):

40A toto,xixi,saxa
40B lala,sojo,huhu
40C sasa,sisi,lala

基于他们的共同列(clade),我想比较A.tsv来自模板的突变B.tsv。我有两个问题:

  1. B.tsv如何在名为“missing_mutation”的新列中指示 中不存在的突变名称A.tsv
  2. 如何在名为“remaining_mutation”的新列中指示存在于A.tsv(以字母 开头s,不区分大小写)但不存在于 中的突变的名称B.tsv

C.tsv看起来像这样:

id  clade   mutation    missing_mutation    remaining_mutation
243 40A siti,toto,mumu  xixi,saxa   siti
254     
267 40B lala,sisi,sojo  huhu    sisi

我知道如何比较两个文件,如下所示:

awk -F"," -vOFS="," '    
    NR==FNR {
     a[$2]=$3;
     next
    }
    
    { print $0,a[$2] }
' B.tsv A.tsv > C.tsv

但我不知道如何打印那些不匹配的内容。你有好主意吗?

答案1

下面的awk程序为了方便使用了 GNU awk(以“真正的双索引数组”的形式),应该可以解决这个问题。它旨在以您的两个文件作为参数按顺序调用B.txt A.txt(就像您在代码示例中所做的那样)。

#!/bin/awk -f

BEGIN{FS=OFS="\t"}

NR==FNR {
    n_tmp[$1]=split($2,buff,/,/)
    for (i=1;i<=n_tmp[$1];i++) mut_tmp[$1][i]=buff[i]
    next
}

FNR==1 {
    print $0,"missing_mutation","remaining_mutation"
}

FNR>1 {
    n_curr=split($3,mutations,/,/)
    mut_miss=0
    mut_rem=0

    # Find missing
    for (j=1;j<=n_tmp[$2];j++)
    {
        for (i=1;i<=n_curr;i++)
        {
            if (mutations[i]==mut_tmp[$2][j]) break
        }
        if (i>n_curr) mut_miss=mut_miss ? mut_miss "," mut_tmp[$2][j] : mut_tmp[$2][j]
    }

    # Find remaining
    for (i=1;i<=n_curr;i++)
    {
        for (j=1;j<=n_tmp[$2];j++)
        {
            if (mutations[i]==mut_tmp[$2][j]) break;
        }
        if (j>n_tmp[$2] && mutations[i]~/^[Ss]/) mut_rem=mut_rem ? mut_rem "," mutations[i] : mutations[i]
    }
    
    if (!mut_miss) mut_miss=""
    if (!mut_rem) mut_rem=""

    print $0,mut_miss,mut_rem
}
  • 这将首先创建一个 clade nr 的关联表。与解析模板时的突变列表相比B.tsv,其中模板突变列表被显式分割成单个突变的数组。请注意,为了方便起见,我使用“双索引”数组mut_tmp,其中第一个索引是分支编号。第二个是突变的索引号。突变的数量也存储在一个数组中n_tmp
  • 解析时A.tsv,它会首先打印新的标头。
  • 然后,对于每一行,它首先将字段 3 中的突变列表拆分为一个数组mutations
  • 然后,它将当前进化枝 ( ) 的模板突变列表中的每个条目mut_tmp[$2]与存在的突变 ( mutations) 进行比较,以填充“缺失突变”字段。
  • 接下来,它将当前突变列表中的每个条目与当前进化枝的模板突变进行比较,以填充“剩余突变”字段,此外还检查突变是否以s或开头S

如果您没有可用的 GNU ,您可以通过替换awk使其与大多数其他实现一起使用awk

mut_tmp[a][b]

mut_tmp[a,b]

这将起作用,因为进化枝编号和突变名称似乎不包含特殊字符\034,默认情况下该特殊字符用于将各个组件连接到单字符串数组索引(查找SUBSEP内部变量以获取更多信息)。

输入示例的输出是:

~$ awk -f compare.awk B.tsv A.tsv 
id  clade   mutation    missing_mutation    remaining_mutation
243 40A siti,toto,mumu  xixi,saxa   siti
254             
267 40B lala,sisi,sojo  huhu    sisi

请注意,您的示例所需的输出与您的需求描述不一致。

编辑

由于您在评论中指出,实际上A.tsv进化枝位于第 22 列,突变位于第 41 列,因此请更改

  • split($3,mutations,/,/)split($41,mutations,/,/)
  • 所有出现n_tmp[$2]n_tmp[$22]
  • mut_tmp[$2][j](或[$2,j]) 到mut_tmp[$22][j](或[$22,j], 分别出现)的所有出现

答案2

选择awk

awk 'BEGIN{ FS=OFS="\t" }
NR==FNR{ mutations[$1] =$2; next }

FNR>1  {
         split($3, muts, "," );
         for(x in muts) { 
             if (gsub( ",?"muts[x]",?", "", mutations[$2])>0) delete muts[x] }
       }

FNR==1 { $4="missing_mutation"; $5="remaining_mutation" }

{ printf ("%s", $0 OFS mutations[$2] OFS );
  for(r in muts) {
      if(muts[r] ~/^[Ss]/) printf("%s", sep muts[r]); sep="," }
  print ""; sep=""
}' fileB  fileA

相关内容