【问题标题】:Merging multiple files with two common columns, and replace the blank to 0合并具有两个公共列的多个文件,并将空白替换为0
【发布时间】:2020-09-27 23:56:18
【问题描述】:

如果有人能帮助我将多个文件(最多 8 个)与两个公共列 ($1$2) 合并,我将不胜感激。我想获取 $3 的所有值并将空白替换为 0。这是来自 4 个文件的示例

文件1:

chr1 111001 234
chr2 22099  108

文件2:

chr1 111001 42
chr1 430229 267

文件3:

chr1 111001 92
chr5 663800 311

文件4:

chr1 111001 129
chr2 22099  442

期望的输出

chr1 111001 234 42 92 129
chr1 430229  0  267 0  0
chr2 22099  108 0  0  442
chr5 663800  0  0 311  0

我试过了

awk '{ a[$1 OFS $2 FS] = a[$1 OFS $2 FS] ( a[$1 OFS $2 FS] == "" ? "" : OFS) $3 }END{ for (i in a){print i,"0",a[i]} }' OFS="\t"  file1.txt file2.txt file3.txt file4.txt | sort -k1

输出

chr1    111001  0   234 42  92  129
chr1    430229  0   267
chr2    22099   0   108 442
chr5    663800  0   311

提前非常感谢

【问题讨论】:

  • 请在您的问题中以代码的形式添加您的努力,强烈建议您这样做。
  • 太糟糕了join 一次只能处理 2 个文件。
  • chr1 111001 可以在同一个文件中出现多次吗?
  • 不,每个文件只有一次@anubhava
  • @Shawn,这段代码可以合并多个文件awk '{ a[$1 OFS $2 FS] = a[$1 OFS $2 FS] ( a[$1 OFS $2 FS] == "" ? "" : OFS) $3 }END{ for (i in a){print i,a[i]} }'

标签: bash unix awk


【解决方案1】:

还有一个变种,请您尝试使用所示示例进行跟踪、编写和测试。

awk '
{
  if(!a[FILENAME]++){
     file[++count]=FILENAME
  }
  b[$1 OFS $2 OFS FILENAME]=$NF
  c[$1 OFS $2]++
  if(!d[$1 OFS $2]++){
    e[++count1]=$1 OFS $2
  }
}
END{
  for(i=1;i<=length(c);i++){
    printf("%s ",e[i])
    for(j=1;j<=count;j++){
      printf("%s %s",(b[e[i] OFS file[j]]!=""?b[e[i] OFS file[j]]:0),j==count?ORS:OFS)
    }
  }
}
' file{1..4} | sort -k1

输出如下。

chr1 111001 234  42  92  129
chr1 430229 0  267  0  0
chr2 22099 108  0  0  442
chr5 663800 0  0  311  0

说明:为上述添加详细说明。

awk '                                        ##Starting awk program from here.
{
  if(!a[FILENAME]++){                        ##Checking condition if FILENAME is present in a then do following.
     file[++count]=FILENAME                  ##Creating file with index of count and value is current file name.
  }
  b[$1 OFS $2 OFS FILENAME]=$NF              ##Creating array b with index of 1st 2nd and filename and which has value as last field.
  c[$1 OFS $2]++                             ##Creating array c with index of 1st and 2nd field and keep increasing its value with 1.
  if(!d[$1 OFS $2]++){                       ##Checking condition if 1st and 2nd field are NOT present in d then do following.
    e[++count1]=$1 OFS $2                    ##Creating e with index of count1 with increasing value of 1 and which has first and second fields here.
  }
}
END{                                         ##Starting END block of this awk program from here.
  for(i=1;i<=length(c);i++){                 ##Starting for loop which runs from i=1 to till length of c here.
    printf("%s ",e[i])                       ##Printing value of array e with index i here.
    for(j=1;j<=count;j++){                   ##Starting for loop till value of count here.
      printf("%s %s",(b[e[i] OFS file[j]]!=""?b[e[i] OFS file[j]]:0),j==count?ORS:OFS)   ##Printing value of b with index of e[i] OFS file[j] if it present then print else print 0, print new line if j==count or print space.
    }
  }
}
' file{1..4} | sort -k1                      ##Mentioning Input_files 1 to 4 here and sorting output with 1st field here.


编辑: 根据 GREAT regex GURU @anubhava sir 的 cmets 添加解决方案与 ARGCARGV 与 GNU awk

awk '
{
  b[$1 OFS $2 OFS FILENAME]=$NF
  c[$1 OFS $2]++
  if(!d[$1 OFS $2]++){
    e[++count1]=$1 OFS $2
  }
}
END{
  count=(ARGC-1)
  for(i=1;i<=length(c);i++){
    printf("%s ",e[i])
    for(j=1;j<=(ARGC-1);j++){
      printf("%s %s",(b[e[i] OFS ARGV[j]]!=""?b[e[i] OFS ARGV[j]]:0),j==count?ORS:OFS)
    }
  }
}
' file{1..4} | sort -k1

【讨论】:

    【解决方案2】:

    你可以使用这个gnu-awk:

    awk 'BEGIN {
       for (k=1; k<ARGC; ++k)
          s = s " " 0
    }
    {
       key=$1 OFS $2
       if (!(key in map))
          map[key] = s
       map[key] = gensub("^( ([0-9]+ ){" ARGIND-1 "})[0-9]+", "\\1" $3, "1", map[key])
    }
    END {
       PROCINFO["sorted_in"]="@ind_str_asc"
       for (k in map)
          print k map[k]
    }' file{1..4} | column -t
    
    chr1  111001  234  42   92   129
    chr1  430229  0    267  0    0
    chr2  22099   108  0    0    442
    chr5  663800  0    0    311  0
    

    说明:

    • 我们正在构建一个全为零的字符串,参数中的每个文件对应一个
    • 使用gensub,我们使用ARGIND(当前参数索引)构建一个正则表达式
    • 此正则表达式将当前ARGINDth 位置的0 替换为$3
    • END 块只打印出存储在 map 中的关联数组内容
    • column -t用于数据的表格显示

    这是一个等效的命令,可以使它在 POSIX awk(非 gnu)中工作:

    awk 'BEGIN {
       for (k=1; k<ARGC; ++k)
          s = s " " 0
    }
    FNR == 1 {
       ++ARGIND
    }
    {
       key=$1 OFS $2
       if (!(key in map))
          map[key] = s
       split(map[key], a)
       a[ARGIND] = $3
       v = ""
       for (k=1; k<ARGC; ++k)
          v = v " " a[k]
       map[key]=v
    }
    END {
       for (k in map)
          print k map[k]
    }' file{1..4}
    

    【讨论】:

    • 非常感谢它完美运行,但我对 ARGIND 参数并不十分熟悉,这对我来说是一个很好的开始
    • 不知道某事是学习新事物的机会,不是吗?顺便说一句,ARGIND 只是第二个 POSIX 代码中的一个变量,我们也可以使用 `var` 代替 ARGIND
    【解决方案3】:

    这些文件看起来像是从 vcf 文件中派生而来的。如果是这样,请不要重新发明轮子。使用任何专门的生物信息学工具来操作这些文件。例如:bedtools, bcftools, Picard MergeVcfs

    通过搜索merge bed filesmerge vcf files 了解更多信息。这些生物信息学工具/包中的大多数都可以使用来自bioconda 频道的conda 进行安装。

    在 bed/vcf 文件被合并/加入/相交/等之后,使用常见的 *NIX 实用程序和脚本语言来提取和处理文件不是任何常见的生物信息学格式.

    【讨论】:

    • 非常感谢您的建议。但是,我真的需要这种脚本,因为我有其他文件也不能用bedtools 处理
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-25
    • 1970-01-01
    • 2019-03-01
    • 1970-01-01
    相关资源
    最近更新 更多