【问题标题】:How to parse values with AWK when column number is inconsistent列号不一致时如何使用 AWK 解析值
【发布时间】:2021-08-01 02:46:45
【问题描述】:

输入文件:

6       31236622        HLA_C*05:01:01:01       A       T       .       PASS    AF=0.07724;MAF=0.07724;R2=0.98466;IMPUTED       GT:DS:HDS:GP    1|0:0.999:0.999,0.000:0.001,0.999,0.000 0|0:0:0,0:1,0,0 1|1:1.994:0.995,1.000:0.000,0.006,0.994
6       29910248        HLA_A*01:01     A       T       .       PASS    AF=0.15969;MAF=0.15969;R2=0.97333;IMPUTED       GT:DS:HDS:GP    0|0:0:0,0:1,0,0 1|0:1.000:1.000,0.000:0.000,1.000,0.000 0|0:0:0,0:1,0,0 
6       31322134        HLA_B*55:01     A       T       .       PASS    AF=0.01091;MAF=0.01091;R2=0.94511;IMPUTED       GT:DS:HDS:GP    0|0:0:0,0:1,0,0 0|0:0:0,0:1,0,0 0|0:0:0,0:1,0,0
6       31322132        HLA_B*55        A       T       .       PASS    AF=0.01091;MAF=0.01091;R2=0.94485;IMPUTED       GT:DS:HDS:GP    0|0:0:0,0:1,0,0 0|0:0:0,0:1,0,0 0|0:0:0,0:1,0,0
6       31322006        HLA_B*44:02:01:01       A       T       .       PASS    AF=0.08074;MAF=0.08074;R2=0.97706;IMPUTED       GT:DS:HDS:GP    1|0:0.999:0.999,0.000:0.001,0.999,0.000 0|0:0:0,0:1,0,0 1|1:1.997:0.998,0.999:0.000,0.003,0.997

我想从“GT:DS:HDS:GP”列之后的每一列中解析一个特定的数字,特别是“x|x:”之后的数字。所以想要的输出是:

0.999, 0, 1.994
0, 1.000, 0
0, 0, 0
0, 0, 0
0.999, 0, 1.997

要从(例如)第 4 行解析所需的值,我可以使用:

awk -F: '{for (i=5; i<=NF; i+=3) printf "%s%s", $i, (i+3 <= NF ? ", " : ORS)}'

第 5 行需要:

awk -F: '{for (i=9; i<=NF; i+=3) printf "%s%s", $i, (i+3 <= NF ? ", " : ORS)}'

所以输入文件的问题是第 3 列(空格分隔)包含可变数量的冒号,这使得冒号成为这个特定输入文件的不良分隔符(但所需的值被冒号包围!)

我虽然关于使用“|”作为分隔符,使用 substr($i,3,?),但所需值的位数不一致(因此是“?”)。

是否有灵活的 awk 代码来获得所需的输出?

【问题讨论】:

  • 感谢您的努力,能否请您突出显示示例输出以更清晰,谢谢。
  • 这是bcftools query的工作
  • @Pierre 我真的很想知道如何使用 bcftools 解析这些剂量。你能详细说明/提供代码吗?谢谢
  • (未测试)bcftools query -u -f '[%DS %HDS %GP]\n'
  • 是的,正如@Pierre 建议使用正确的工具来操作 VCF 文件,这是我对类似问题的回答:stackoverflow.com/a/59104151/680068stackoverflow.com/a/54305323/680068

标签: parsing awk bioinformatics


【解决方案1】:

你可以试试这个awk:

awk -v OFS=', ' '$9 == "GT:DS:HDS:GP" {for (i=10; i<=NF; ++i) if ($i ~ /^[0-9]+\|[0-9]+:/ && split($i, a, /:/)) printf "%s", (i == 10 ? "" : OFS) a[2]; print ""}' file

0.999, 0, 1.994
0, 1.000, 0
0, 0, 0
0, 0, 0
0.999, 0, 1.997

扩展形式:

awk -v OFS=', ' '
$9 == "GT:DS:HDS:GP" {
   for (i=10; i<=NF; ++i)
      if ($i ~ /^[0-9]+\|[0-9]+:/ && split($i, a, /:/))
         printf "%s", (i == 10 ? "" : OFS) a[2]
   print ""
}' file

【讨论】:

    【解决方案2】:

    您为什么要关心以空格分隔的列?

    awk '{ sub(/.* GT:DS:HDS:GP */, "");
        i = split($0, n, /[0-9]\|[0-9]:/);
        sep = "";
        for(x=2; x<=i; x++) {
            sub(/:.*/, "", n[x]); printf("%s%s", sep, n[x]); sep=", " }
        printf "\n"; }' file
    

    我们依次将每一行分开,首先从行中删除通过GT:DS:HDS:GP 的所有内容,然后将剩余的字符串拆分为指定分隔符上的n,然后通过删除第一个之后的所有内容来清理结果字段每个冒号,并打印结果。 (我们跳过第一个,它只包含第一个分隔符之前无用的短字符串或空字符串。)

    样本的输出:

    0.999, 0, 1.994
    0, 1.000, 0
    0, 0, 0
    0, 0, 0
    0.999, 0, 1.997
    

    我不知道这些字段代表什么,所以我只选择了单字母变量名;您可能可以通过为这些变量提供更具描述性的名称来提高可读性。

    【讨论】:

      猜你喜欢
      • 2015-01-16
      • 2010-11-29
      • 1970-01-01
      • 2012-12-24
      • 1970-01-01
      • 2018-04-13
      • 2019-04-05
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多