当列号不一致时如何使用 AWK 解析值

How to parse values with AWK when column number is inconsistent

输入文件:

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 列(space 分隔)包含可变数量的冒号,这使得冒号对于这个特定的输入文件来说是一个糟糕的分隔符(但所需的值被包围冒号!)

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

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

您为什么要关心 space 分隔的列?

awk '{ sub(/.* GT:DS:HDS:GP */, "");
    i = split([=10=], 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

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

你可以试试这个awk:

awk -v OFS=', ' ' == "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=', ' '
 == "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