awk 中循环行和列以计算每个单元格的特定子字符串

awk 中循环行和列以计算每个单元格的特定子字符串

我有一个 .vcf 文件,其中包含 138 个第一标题行(以 # 开头)和其他数据(行中的 snp(322045)以及列中包含一些信息的患者(前 10 行)。我使用脚本 bash 来计算每个row 该行中与“0|0”不同(在初始部分)的单元格数量:这是我的脚本

for j in {139..322045}

    do
     c=0
     awk -v var=$c -v j=$j 'NR==j{for(i=10; i<=NF; i++) {if(substr($i,1,3)!="0|0") var++}} END{ print $1 ":" $2 "\t" var }' file.vcf >> out.txt
    done

这是输入

> #<info>
> #..
> # . . . 
21  9411245  x  C   A   505  PASS   AC=2   GT:AD:DP:GQ:PL   0|0:11  0|0:12
21  9411246  y  C   T   505  PASS   AC=2   GT:AD:DP:GQ:PL   0|0:11  1|0:13

(各列以制表符分隔)然后我打印由 : 链接的第一列和第二列以及计数;但它不能完全工作,如果我使用仅包含 2 行的子集,它可以完美工作。这是结果

21:48111872 2
21:48111872 1
21:48111872 0
21:48111872 2

它重复行

我该如何解决它?提前致谢,如果您修复了它,请写一个简短的解释。

注意计算它需要很多时间(也用于 {139..160})

答案1

它不起作用的原因是您正在打印$1$2在块中END{}END{}仅在读取输入文件的最后一行后运行一次。所以$1$2永远是最后一行的第一个和第二个字段。

无论如何,这是一种极其低效的解析文本文件的方法。您正在为循环的每次迭代阅读整个内容。 shell 循环是非常慢的。所以你正在使用一个非常慢的循环你不必要地一遍又一遍地阅读 awk 中的数千行。

不使用 shell 循环,只需在 awk 中执行所有操作:

$ awk -F"\t" '/^[^#]/{var=0; for(i=10; i<=NF; i++) {if(substr($i,1,3)!="0|0") var++} print $1 ":" $2 "\t" var }' foo.vcf 
21:9411245  0
21:9411246  1

或者,稍微简洁一些:

awk -F"\t" '/^[^#]/{
        var=0; 
        for(i=10; i<=NF; i++) {
            if(substr($i,1,3)!="0|0"){
                var++
            } 
        }
        print $1 ":" $2 "\t" var 
    }' foo.vcf 

解释

  • -F"\t":将输入字段分隔符设置为制表符。
  • /^[^#]/{ ... }:仅对以非( ) 字符开头的行(/^a/将匹配以 开头的行)执行此操作。a#[^#]
  • var=0;:将var每个输入行设置回 0。
  • for(i=10; i<=NF; i++) {if(substr($i,1,3)!="0|0") var++}:这是您的原始代码,它计算发现非 基因型的次数0|0
  • print $1 ":" $2 "\t" var:再次,您的代码,但现在在END{}块之外,因此它在每一行上运行,而不仅仅是末尾。

就是这样。不需要 shell 循环,并且只需几秒钟。

相关内容