Bash

在 awk 中循環行和列以計算每個單元格的特定子字元串

  • November 12, 2019

我有一個 .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

(這些列是製表符分隔的)然後我列印由 : 連結的第 1 列和第 2 列以及計數;但它不能完全工作,如果我使用一個只包含 2 行的子集,它工作得很好。這是結果

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

它重複行

我該如何解決?在此先感謝,如果您修復它,請寫一個簡短的解釋。

注意計算它需要很多時間(也用於 {139..160})

它不起作用的原因是您正在列印$1並且$2END{}塊中。END{}僅在讀取輸入文件的最後一行後執行一次。所以$1並且$2將永遠是最後一行的第一個和第二個欄位。

無論如何,這是一種非常低效的解析文本文件的方法。您正在閱讀循環的每次迭代的全部內容。而且外殼循環非常慢。因此,您正在使用一個非常慢的循環*,*並且一遍又一遍地不必要地閱讀 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 循環,它應該只需要幾秒鐘。

引用自:https://unix.stackexchange.com/questions/551847