当列号不一致时如何使用 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
输入文件:
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