第十章 使用序列數據
生物信息學的核心問題之一是處理大量的(通常定義糟糕或模糊)文件格式。久而久之,一些特定的簡單的人類可讀的格式成為了事實上的標準。
- 彼得·科克等, 2010年
好的程序員知道怎樣寫程序。杰出的程序員知道怎樣重寫(并重新使用)。- 大教堂和集市 埃里克·斯蒂芬·雷蒙德
在生物信息學中,核苷酸(和蛋白質)序列普遍以兩種純文本格式存儲:FASTA和FASTQ,發(fā)音分別為fast-ah(或fast-A)和fast-Q。 我們將在本節(jié)討論這兩種格式和它們各自的局限性,然后了解一些處理這些數據格式的工具。這是一個簡短的章節(jié),但是本節(jié)有一個重要的事項需要注意,也就是處理這些特定生物信息學格式的共同誤區(qū)。即使像文件格式這樣的細枝末節(jié)中存在的簡單錯誤,也需要很多時間和精力去發(fā)現(xiàn)和修復,所以請盡早記住這些細節(jié)。
FASTA格式
FASTA格式源于由William R. Pearson和David J. Lipman開發(fā)的FASTA比對套裝軟件。FASTA格式用于存儲各種序列數據,不需要每個堿基對的質量分數。這些序列數據包括參考基因組文件,蛋白質序列,DNA編碼序列(CDS),轉錄本序列等。FASTA也可以用來存儲多序列比對數據,但是我們這里不會討論這個FASTA的格式的特殊變體。我們在前面的章節(jié)遇到了FASTA格式,但在本節(jié)中,我們將詳細介紹這種格式,了解該格式常見的缺陷,并介紹一些處理這種格式的工具。
FASTA文件由序列條目組成,每個條目都包含兩部分內容:序列描述和序列數據。描述行以一個大于號(>)開始,包含序列標識符和其他(可選)信息。 序列數據從描述行的下一行開始,直到另一個描述行(以 > 開頭的行)出現(xiàn)或文件結束。 本章GitHub目錄中的 egfr_ FL ank.fasta 文件是一個FASTA例子文件:
$ head -10 egfr_flank.fasta
> ENSMUSG00000020122 | ENSMUST00000138518
CCCTCCTATCATGCTGTCAGTGTATCTCTAAATAGCACTCTCAACCCCCGTGAACTTGGT
TATTAAAAACATGCCCAAAGTCTGGGAGCCAGGGCTGCAGGGAAATACCACAGCCTCAGT
TCATCAAAACAGTTCATTGCCCAAAATGTTCTCAGCTGCAGCTTTCATGAGGTAACTCCA
GGGCCCACCTGTTCTCTGGT
> ENSMUSG00000020122 | ENSMUST00000125984
GAGTCAGGTTGAAGCTGCCCTGAACACTACAGAGAAGAGAGGCCTTGGTGTCCTGTTGTC
TCCAGAACCCCAATATGTCTTGTGAAGGGCACACAACCCCTCAAAGGGGTGTCACTTCTT
CTGATCACTTTTGTTACTGTTTACTAACTGATCCTATGAATCACTGTGTCTTCTCAGAGG
CCGTGAACCACGTCTGCAAT
FASTA格式的簡單性和靈活性卻帶來了令人遺憾的缺點:FASTA格式是一個松散定義的特殊格式(雖然這種現(xiàn)象在生物信息學中十分常見)。因此,你可能會遇到FASTA格式的各種變體,這些變體會造成微小的錯誤,除非你的程序足夠強大。這就是為什么最好使用現(xiàn)有的FASTA/FASTQ解析庫而不是執(zhí)行自定義的解析庫;現(xiàn)有的庫已經過開源社區(qū)審查(后續(xù)將更多討論這一部分內容)。
關于FASTA格式最令人頭疼的是描述行中標識符的格式沒有通用的規(guī)范。例如,如下FASTA描述行是否指代同一條目?
>ENSMUSG00000020122|ENSMUST00000138518
> ENSMUSG00000020122|ENSMUST00000125984
>ENSMUSG00000020122|ENSMUST00000125984|epidermal growth factor receptor
>ENSMUSG00000020122|ENSMUST00000125984|Egfr
>ENSMUSG00000020122|ENSMUST00000125984|11|ENSFM00410000138465
沒有標識符的標準方案,我們不能使用簡單的精確匹配確定一個標識符是否和FASTA條目的標題行匹配。相反,我們需要依靠FASTA文件描述行和標識符之間的模糊匹配。這可能會使得判斷標準變得相當凌亂:匹配模式的寬容度應該是多少?如果正則表達式過于寬泛是否會匹配到錯誤的序列?基本上來說,模糊匹配是一個脆弱的策略。
幸運的是,這個問題有更好的解決方案(也很簡單):與其事后依靠模糊匹配來糾正不一致的命名,還不如一開始就擁有嚴格的命名規(guī)范并始終保持一致。然后,在運行任何外部來源的數據時,需要通過幾個合理性檢查,以確保數據遵循對應的格式。 這些檢查不需要太復雜(檢查重復的名稱,手動檢查一些條目,檢查 > 和標識符之間錯誤的空格,檢查不同文件之間名字的重合等)。
如果你需要整理外部數據,請始終保留原始文件并編寫腳本將更正的版本寫入新的文件。這樣,腳本可以輕松地重新運行,用于處理你獲得的新版本的原始數據集(但你仍然需要檢查每一項內容-不要盲目信任數據!)。
常見的命名規(guī)范是將描述行利用第一個空格分為兩部分:標識符和注釋。此格式的序列通常如下所示:
> gene_00284728 length = 231; type = dna
GAGAACTGATTCTGTTACCGCAGGGCATTCGGATGTGCTAAGGTAGTAATCCATTATAAGTAACATGCGCGGAATATCCG
GAGGTCATAGTCGTAATGCATAATTATTCCCTCCCTCAGAAGGACTCCCTTGCGAGACGCCAATACCAAAGACTTTCGTA
GCTGGAACGATTGGACGGCCCAACCGGGGGGAGTCGGCTATACGTCTGATTGCTACGCCTGGACTTCTCTT
這里 gene_00284728 是標識符,length = 231; type = dna 是注釋。此外,ID應該是唯一的。盡管不是標準規(guī)范,但是將第一個空格之前的內容作為標識符,之后的內容作為非必要注釋在生物信息學程序中是很常見的(例如,BEDtools,Samtools和BWA都是這樣處理的)。有了這個規(guī)范以后,通過標識符查找一個特定的序列會很容易,我們將在本章末尾討論如何高效地利用這種方式處理經過索引的FASTA文件。
FASTQ格式
FASTQ格式通過給序列中每個堿基添加數字質量分數的形式擴展了FASTA文件。FASTQ格式被廣泛用于存儲高通量測序數據,該數據利用每個堿基的質量分數來表示每個堿基測序的可信度。然而和FASTA格式一樣,F(xiàn)ASTQ格式也擁有變體和缺陷,會使看似簡單的格式變得很難處理。
FASTQ格式如下所示:
@ DJB775P1:248:D0MDGACXX:7:1202:12362:49613 ①
TGCTTACTCTGCGTTGATACCACTGCTTAGATCGGAAGAGCACACGTCTGAA ②
+ ③
JJJJJIIJJJJJJHIHHHGHFFFFFFCEEEEEDBD?DDDDDDBDDDABDDCA ④
@ DJB775P1:248:D0MDGACXX:7:1202:12782:49716
CTCTGCGTTGATACCACTGCTTACTCTGCGTTGATACCACTGCTTAGATCGG
+
IIIIIIIIIIIIIIIHHHHHHFFFFFFEECCCCBCECCCCCCCCCCCCCCCC
① 描述行,以@符號開始。包含標識符記錄和其他信息。
② 序列數據,可以是一行或多行。
③ 以 + 開頭的行,位于序列數據之后,表示序列的結束。在舊版本的FASTQ文件中,通常會這里重復描述行,但這是多余的,會導致不必要的FASTQ文件過大。
④ 質量數據,也可以是一行或多行,但必須和序列長度相同。每個堿基質量數值以ASCII字符編碼,編碼規(guī)則我們將在后文討論(“堿基質量”見344頁)。
與FASTA文件一樣,F(xiàn)ASTQ文件中一個常見的規(guī)范是將描述行利用第一個空格分解為兩部分:標識符記錄和注釋。
如何正確地解析FASTQ文件其實是非常棘手的。一個常見的誤區(qū)是將每一個以 @ 符號開始的行都當作說明行。然而,@ 本身也是一個有效的質量符號。FASTQ的序列行和質量行可以延續(xù)到下一行,所以以 @ 符號開頭的行可能是質量行,而不是標題行。所以,寫一個始終把以 @ 開頭的行作為標題行的解析程序會導致不正確的解析。但是,我們可以利用質量分數的字符數必須等于序列的字符數這一準則來準確解析這種格式,這其實就是后文中 readfq 解析器的工作原理。
計算FASTA/FASTQ條目的輸入和輸出
作為純文本格式,可以輕松使用Unix工具處理FASTQ和FASTA文件。一個常見的生物信息學命令行是這樣的:
$ grep -c "^>" egfr_flank.fasta
5
正如本書47頁“Pipes實戰(zhàn):利用grep和pipes創(chuàng)建簡單的程序”所說,你必須引用** > **字符來防止shell把它當作一個重定向運算符(并覆蓋你的FASTA文件!)。這是一個保險的計算FASTA文件數目的方式,因為雖然格式定義寬松,但是每個序列都具有一個描述行,只有這些描述行以 > 符號開始 。
我們可能會嘗試使用類似的方法處理FASTQ文件,用 @ 符號代替 >:
$ grep -c "^@" untreated1_chr4.fq
208779
這意味著 untreated1_chr4.fq 有208779個條目。但是,仔細檢查 untreated1_chr4.fq 文件,你會發(fā)現(xiàn)每個FASTQ條目占用四行,但總行數是:
$ wc -l untreated1_chr4.fq
817420 untreated1_chr4.fq
而817420/4 = 204355,這和 grep -c
命令得到的結果有很大不同!這是發(fā)生了什么呢?請記住,@ 是一個有效的質量符號,質量行可以以這個符號開始,可以使用 grep "^@" untreated1_chr4.fq | less
命令行來查看這樣的例子。
如果你很確定你的FASTQ文件每個序列條目占用四行,可通過 wc -l
命令估計文件行數并除以4的方式來估計序列條目。如果你不確定某些FASTQ條目是否包含多行,一個更強大的計算序列條目的方法是bioawk:
$ bioawk -cfastx 'END {print NR}' untreated1_chr4.fq
204355
核苷酸編碼
隨著基礎FASTA / FASTQ格式的覆蓋,讓我們來看看以這些格式出現(xiàn)的核苷酸和堿基質量分數的標準編碼。 顯然,編碼核苷酸很簡單:A,T,C,G分別代表核苷酸腺嘌呤,胸腺嘧啶,胞嘧啶和鳥嘌呤。小寫的堿基經常被用來代表“軟覆蓋”重復序列或低復雜度序列(RepeatMasker 和 Tandem Repeats Finder 程序均采用這種格式)。重復序列和低復雜度的序列也可以被“硬覆蓋”,核苷酸序列被替換為N(有時是X)。
簡并(或模糊)核苷酸編碼被用于表示兩個或多個堿基。例如,N可以用于表示任何堿基。國際純粹與應用化學聯(lián)合會(IUPAC)擁有一組標準化的核苷酸編碼,既包括明確的也包括簡并的(見表10-1)。
表 10-1 IUPAC 核苷酸代碼
IUPAC代碼 | 堿基 | 助記符 |
---|---|---|
A | Adenine | 腺嘌呤 |
T | Thymine | 胸腺嘧啶 |
C | Cytosine | 胞嘧啶 |
G | Guanine | 鳥嘌呤 |
N | A, T, C, G | 任何堿基 |
Y | C, T | 嘧啶 |
R | A, G | 嘌呤 |
S | G, C | 強鍵 |
W | A, T | 弱鍵 |
K | G, T | 酮基 |
M | A, C | 氨基 |
B | C, G, T | A以外的所有堿基,位于A堿基后 |
D | A, G, T | C以外的所有堿基,位于C堿基后 |
H | A, C, T | G以外的所有堿基,位于G堿基后 |
V | A, C, G | T或U(尿嘧啶)以外的所有堿基,位于U堿基后 |
有些生物信息學程序對于簡并核苷酸的處理可能會有所不同。例如,BWA讀長比對工具可以將參考基因組中的簡并核苷酸字符轉換為隨機堿基(Li and Durbin, 2009),但是由于使用了隨機種子集合,所以不會在重新索引比對數據的時候產生兩種不同的結果。
堿基質量
FASTQ條目的每個序列堿基在質量行都具有相應的數字質量分數。每個堿基的質量分數都編碼為一個ASCII字符。質量行看起來就像一串隨機字符,如第四行所示:
@ AZ1:233:B390NACCC:2:1203:7689:2153
GTTGTTCTTGATGAGCCATGAGGAAGGCATGCCAAATTAAAATACTGGTGCGAATTTAAT
+
CCFFFFHHHHHJJJJJEIFJIJIJJJIJIJJJJCDGHIIIGIGIJIJIIIIJIJJIJIIH
(這個FASTQ條目在本章的 README 文件中,如果你想按照這里所說的跟著一起看看。)
記住,ASCII字符僅在內部表示為0到127之間的整數(詳情參見 man ascii
命令)。因為并不是所有的ASCII字符都可以在屏幕上顯示出來(例如,字符呼應 "\ 07" 會發(fā)出“叮”的聲音),質量分數僅限于可打印的ASCII字符,范圍從33到126(代表空格的字符32被省略)。
所有編程語言都具有將字符轉換為十進制ASCII碼和將十進制ASCII碼轉換為字符的功能。在Python中,分別對應函數 ord() 和 chr()。讓我們使用 ord() 函數在Python交互式解釋器中將質量字符轉換為十進制ASCII碼列表:
>>> qual = "JJJJJJJJJJJJGJJJJJIIJJJJJIGJJJJJIJJJJJJJIJIJJJJHHHHHFFFDFCCC"
>>> [ord(b) for b in qual]
[74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 74, 71, 74, 74, 74, 74, 74, 73,
73, 74, 74, 74, 74, 74, 73, 71, 74, 74, 74, 74, 74, 73, 74, 74, 74, 74, 74,
74, 74, 73, 74, 73, 74, 74, 74, 74, 72, 72, 72,
67, 67, 67]
遺憾的是,將這些ASCII數值轉換成有意義的質量分數可能非常棘手,因為目前存在三種不同的質量體系:Sanger,Solexa和Illumina(見表10-2)。負責Biopython,BioPerl和BioRuby這類項目的開放性生物信息學基金會(OBF),將它們命名為 fastq-sanger,fastq-solexa 和 fastaq-illumina。值得慶幸的是,生物信息學領域最終似乎確定統(tǒng)一使用Sanger編碼(也就是這里顯示的質量行格式),因此我們將使用此方案逐步完成轉換過程。
表 10-2 FASTQ質量體系(來源 Cock et al., 2010,已獲得許可)
名稱 | ASCII字符范圍 | 偏移 | 質量分數類型 | 質量分數范圍 |
---|---|---|---|---|
Sanger, Illumina(版本1.8以上) | 33-126 | 33 | PHRED | 0-93 |
Solexa, 早期Illumina(版本1.3之前) | 59-126 | 64 | Solexa | 5-62 |
Illumina(版本1.3-1.7) | 64-126 | 64 | PHRED | 0-62 |
首先,我們需要減去一個偏移值來將這個Sanger質量分數轉換為PHRED質量分數。PHRED是由Phil Green撰寫的早期堿基調用程序,用于解析他自己寫的熒光追蹤數據。通過查看表10-2,我們注意到Sanger格式的偏移量為33,因此我們從每個質量分數中減去33:
>>> phred = [ord(b)-33 for b in qual]
>>> phred
[41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 38, 41, 41, 41, 41, 41, 40,
40, 41, 41, 41, 41, 41, 40, 38, 41, 41, 41, 41, 41, 40, 41, 41, 41, 41, 41,
41, 41, 40, 41, 40, 41, 41, 41, 41, 39, 39, 39, 39, 39, 37, 37, 37, 35, 37,
34, 34, 34]
現(xiàn)在,鑒于我們的Sanger質量分數已經轉換為PHRED質量分數,我們就可以利用下面的公式將質量分數轉換為堿基測序正確的估計概率: 將概率轉化為質量,我們使用這個函數的倒數:
在我們的例子中,我們需要前一個方程式。將其應用于我們的PHRED質量分數:
>>> [10**(-q/10) for q in phred]
[1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05,
1e-05, 0.0001, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 0.0001, 0.0001, 1e-05,
1e-05, 1e-05, 1e-05, 1e-05, 0.0001, 0.0001, 1e-05, 1e-05, 1e-05, 1e-05,
1e-05, 0.0001, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 1e-05, 0.0001,
1e-05, 0.0001, 1e-05, 1e-05, 1e-05, 1e-05, 0.0001, 0.0001, 0.0001, 0.0001,
0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001, 0.0001]
Illumina(版本1.3至1.7)之間的質量數據轉換是一個相同的過程,除了我們使用偏移量64(見表 10-2)。Solexa轉換就有點棘手了,因為該方案不使用PHRED函數映射質量分數和概率。相反,它使用公式。關于這個格式的更多詳情,請參閱Cock et al., 2010。
示例:檢查和去除低質量堿基
請注意前一個例子中堿基的準確度是如何下降的;這是Illumina測序的特征誤差分布。本質上來說,我們通過這種測序技術獲得的讀長的堿基測序錯誤概率逐漸增加(朝向3'端)。這可能對下游分析產生深遠的影響!當處理測序數據時,你應該總是
- 了解測序技術的錯誤分布和局限性(例如,是否受到GC含量的影響)
- 考慮上述內容將如何影響你的分析
所有這一切都是實驗特異性的,而且需要精心的規(guī)劃。
把我們關于堿基準確度的Python列表作為一個學習工具是很用的,可以看到如何將概率轉換為質量,但它不能幫助我們了解數百萬序列的質量概況。從這個意義上來說,一圖抵千字-有軟件可以幫助我們看到讀長中堿基的質量分布。最流行的是Java程序 FastQC,這款軟件很容易運行并輸出有用的圖形和質量指標。如果你喜歡使用R語言,你可以使用Bioconductor中的軟件包 qrqc(由鄙人所寫)。我們將在示例中使用 qrqc 軟件包,這樣我們就可以自己了解如何可視化這些數據。
我們先來安裝運行這個例子所有必要的程序。首先,利用如下命令行在R中安裝 qrqc 軟件包:
> library(BiocInstaller)
> biocLite('qrqc')
接下來,讓我們來安裝兩個程序用來去除低質量堿基:sickle 和 seqtk 。seqtk 是由Heng Li編寫的一個通用序列工具包 - 擁有在序列末端去除低質量堿基的子命令(還有許多有用的其他功能)。利用Homebrew軟件包管理系統(tǒng),sickle 和 seqtk 很容易在Mac OS X操作系統(tǒng)上安裝(例如,利用brew install seqtk
和 brew install sickle
命令行)。
在安裝好這些程序之后,讓我們來開始修整在GitHub存儲庫中本章目錄下的 untreated1_chr4.fq FASTQ文件。這個FASTQ文件由Bioconductor中 pasillaBamSubset 軟件包的 untreated1_chr4.bam BAM文件生成(更多信息請參見本章目錄中的 README 文件)。為了保持事情簡單,我們將使用每個程序的默認設置。從 sickle 軟件包開始:
$ sickle se -f untreated1_chr4.fq -t sanger -o untreated1_chr4_sickle.fq
FastQ records kept: 202866
FastQ records discarded: 1489
sickle 通過 -f 指定輸入文件,通過 -t 指定質量分數類型,通過 -o 得到修整后的文件。
現(xiàn)在,讓我們來運行 seqtk trimfq
命令,這個命令需要一個參數并以標準形式輸出修整后的序列:
$ seqtk trimfq untreated1_chr4.fq > untreated1_chr4_trimfq.fq
讓我們在R中比較上述結果。我們使用 qrqc 軟件包根據位置收集這些文件中的堿基質量分布,然后使用 ggplot2 軟件包可視化這些結果。我們可以逐一加載這些軟件包,但一個不錯的工作流程是利用 lapply() 函數自動執(zhí)行:
# trim_qual.R -- 檢查修剪前后堿基質量
library(qrqc)
# FASTQ文件
fqfiles <- c(none="untreated1_chr4.fq",
sickle="untreated1_chr4_sickle.fq",
trimfq="untreated1_chr4_trimfq.fq")
# 使用 qrqc 軟件包的 readSeqFile 函數加載每個文件
# 我們只需要堿基質量,所以關掉
# readSeqFile 函數的一些其他功能。
seq_info <- lapply(fqfiles, function(file) {
readSeqFile(file, hash=FALSE, kmer=FALSE)
})
# 將堿基質量提取為數據框,并追加
# 一列使用修剪工具(或沒有使用)的情況。這部分
# 在后面的作圖中使用。
quals <- mapply(function(sfq, name) {
qs <- getQual(sfq)
qs$trimmer <- name
qs
}, seq_info, names(fqfiles), SIMPLIFY=FALSE)
# 將列表中單獨的數據框合并為單個數據框
d <- do.call(rbind, quals)
# 堿基質量可視化
p1 <- ggplot(d) + geom_line(aes(x=position, y=mean, linetype=trimmer))
p1 <- p1 + ylab("mean quality (sanger)") + theme_bw()
print(p1)
# 使用 qrqc 軟件包的 qualPlot 函數與列表生成面板圖
# 只顯示 10% 到 90% 的分位數和低通曲線
p2 <- qualPlot(seq_info, quartile.color=NULL, mean.color=NULL) + theme_bw()
p2 <- p2 + scale_y_continuous("quality (sanger)")
print(p2)
該腳本會生成兩幅圖:圖10-1和圖10-2。在圖10-2中我們可以同時看到兩個修剪軟件對數據堿基質量分布的影響:通過去除低質量堿基,我們進一步縮小了讀長堿基的質量分布范圍。在圖10-1中,我們可以發(fā)現(xiàn)去除低質量堿基增加了整個讀長的平均質量,但仍然可以看到堿基質量沿著讀長末端逐漸下降。在一行序列中,我們可以從末尾去除低質量的堿基-修剪命令并不困難。更重要的步驟是通過比較去除低質量堿基之前和之后的文件來可視化這些修剪程序對我們的數據做了怎樣的修改。檢查程序如何更改我們的數據而不是盲目相信它們才是強大的生物信息學的重要組成部分,也是遵守黃金法則(不要信任你的工具)的表現(xiàn)。在這個例子中,檢查一小部分數據只需要不超過20行代碼(忽略空白行和提高可讀性的注釋)和幾分鐘的時間,但關于這些程序對我們的數據做了什么和程序之間的差異,這些步驟給了我們有價值的理解。如果我們愿意,我們也可以更改不同的設置來同時運行這兩個質量修飾程序,并比較這些設置對結果的影響。大部分嚴謹的生物信息學都是這樣的過程:運行程序,對比原始數據與輸出結果,運行程序,比較輸出結果,如此往復。
FASTA/FASTQ解析示例:核苷酸計數
編寫你自己的FASTA/FASTQ解析器其實并不是很困難,事實上,這是一個非常有教育性的編程練習。 但是當我們需要使用解析器來處理真實的數據時,最好還是使用可靠的現(xiàn)有庫。記住本章開頭引用的話:杰出的程序員知道何時重用代碼。有很多開源的,免費提供的,可靠的解析器已經經過了開源社區(qū)審核,并且旨在加速解析過程。我們將使用Heng Li的 readfq 應用程序,因為它可以解析FASTA和FASTQ文件,使用起來很簡單,是獨立應用(意味著它不需要安裝任何依賴項)。Biopython和BioPerl這兩個流行的程序庫中有很好的可選FASTA/FASTQ解析器。
我們將使用Python版本的 readfq 程序,readfq.py。你可以從GitHub上下載這個Python文件(本書的GitHub資源庫中也有這個文件的副本)。雖然我們可以使用 from readfq import readfq
命令來執(zhí)行 readfq.py 中的FASTA/FASTQ解析程序,但是對于單文件腳本其實更簡單,你可以直接將程序復制粘貼到你的腳本中。在本書中,我們使用 from readfq import readfq
命令來避免占據例子文件的空間。
readfq 程序的 readfq() 函數使用簡單。readfq() 采用一個文件對象(例如,一個已經通過
open('filename.fa') 或 sys.stdin 打開的文件名稱),并會產生FASTQ/FASTA條目,直到它到達文件或流的末尾。每個FASTQ/FASTA條目由 readfq() 函數返回為包含條目描述,序列和質量的元組。如果 readfq() 正在讀取一個FASTA文件,質量行是無。這就是利用 readfq() 函數讀取FASTQ/FASTA序列行的所有內容。
我們編寫一個簡單的程序來計算文件中每個IUPAC核苷酸的數量:
#!/usr/bin/env python
# nuccount.py -- 一個文件中的所有核苷酸數目
import sys
from collections import Counter ①
from readfq import readfq
IUPAC_BASES = "ACGTRYSWKMBDHVN-." ②
# 初始化計數器
counts = Counter() ③
for name, seq, qual in readfq(sys.stdin): ④
# 對于每一個序列條目,向計數器添加所有的堿基數量
counts.update(seq.upper()) ⑤
#打印結果
for base in IUPAC_BASES:
print base + "\t" + str(counts[base]) ⑥
① 我們使用 collections 模塊中的 Counter 計數核苷酸。Counter 的工作原理很像Python的 dict 類(技術上來說,它是 dict 的一個子類),具有使計數更容易的附加功能。</br>
② 這個全局變量定義了所有的IUPAC核苷酸,我們稍后將用它來打印順序一致的堿基(因為像Python字典一樣,Counter 對象不保持順序)。在腳本的頂部放置類似IUPAC_BASES的大寫常量讓讀者清楚是一個很好的做法。</br>
③ 創(chuàng)建一個新的 Counter 對象。</br>
④ 此行使用 readfq 模塊中的 readfq 函數從文件句柄參數讀取FASTQ/FASTQ條目(在這種情況下,sys.stdin,標準輸入)轉換成 name,seq 和 qual 變量(通過Python中被稱為元組拆封的功能)。</br>
⑤ Counter.update() 方法讀取任何可迭代的對象(在此情況下,序列堿基的字符串),并將它們添加到計數器中。我們也可以使用for循環(huán)遍歷 seq 變量的每個字符,利用 counts[seq.upper()]
命令遞增計數。請注意,我們用 upper() 函數把所有的字符轉換為大寫,因此小寫的“軟覆蓋”堿基也會被計數。</br>
⑥ 最后,我們迭代所有的IUPAC堿基并輸出它們的數量。 </br>
這個版本需要通過標準輸入獲得輸入文件,所以保存此文件并使用 chmod + X nuccount.py
命令添加執(zhí)行權限后,我們可以通過下述命令運行:
$ cat contam.fastq | ./nuccount.py
A 103
C 110
G 94
T 109
R 0
Y 0
S 0
W 0
K 0
M 0
B 0
D 0
H 0
V 0
N 0
- 0
. 0
請注意,我們不一定要讓Python腳本可執(zhí)行;另一個選擇是簡單地用 python nuccount.py 命令啟動它 。 無論哪種方式都會得到相同的結果,所以這最終是一種風格的選擇。但是,如果你確實想讓它變成一個可執(zhí)行的腳本,記住要做以下的事情:
- 包含標識行
#!/usr/bin/env python
- 使用
chmod + X <scriptname.py>
命令使腳本可執(zhí)行
這個腳本有很多可以改進的地方:添加對每個序列堿基組成統(tǒng)計的支持,采用文件參數而不是通過標準輸入,“軟覆蓋”堿基(小寫)計數,CpG位點計數或在文件中發(fā)現(xiàn)非IUPAC核苷酸時發(fā)出警告。核苷酸計數很簡單-腳本中最復雜的部分是 readfq() 函數。這是重用代碼之美:書寫良好的函數和庫讓我們不需要重寫復雜的解析器。相反,我們使用更廣泛的社區(qū)合作開發(fā)的軟件,測試,排除故障,使其更有效率(參見 readfq 軟件在GitHub上的提交歷史作為例子)。重用軟件不是欺騙-這是行家寫程序的方式。
索引FASTA文件
我們經常需要對FASTA文件的子序列進行有效的隨機訪問(給定區(qū)域)。乍一看來,寫一個腳本來完成似乎并不困難。例如,我們可以編寫一個遍歷FASTA條目的腳本,提取與指定區(qū)域重疊的序列。但是,在提取一些隨機序列時,這不是一個有效的方法。為了知道為什么,假設我們需要訪問小鼠基因組第8號染色體(123,407,082至123,410,742區(qū)域的)序列。這種方法會不必要地在內存中解析和加載1號染色體至7號染色體的數據,即使我們不需要從這些染色體中提取子序列。從磁盤中讀取全部染色體并將其復制到內存中可能效率相當低下-我們?yōu)榱颂崛?.6 kb數據必須加載所有125 Mb的8號染色體!從FASTA文件中提取大量隨機子序列可能計算成本相當高昂。
允許容易和快速隨機訪問的常見計算策略是索引文件。索引文件在生物信息學中是普遍存在的;在未來的章節(jié)中,我們將索引基因組,以便它們可以用作比對參考序列,我們將索引包含比對讀長的文件,用于快速隨機訪問。在本節(jié)中,我們將介紹一下索引后的FASTA文件是如何允許我們快速輕松地提取子序列的。一般來說,我們經常索引整個基因組,但為了簡化本章剩余部分的例子,我們只考慮小鼠基因組的第8號染色體。從本章的GitHub的文件目錄中下載 Mus_musculus.GRCm38.75.dna.chromosome.8.fa.gz 文件并用 gunzip
命令解壓。
索引壓縮的FASTA文件
雖然 samtools faidx
命令確實可以處理壓縮的FASTA文件,但是它只適用于BGZF壓縮文件(更深入的
話題,我們將在425頁“快速訪問經過BGZF和Tabix命令索引的制表符分隔的序列文件”進一步討論)。
我們將使用Samtools對該文件進行索引,它是一種廣泛使用的操作SAM和BAM比對格式的工具套件(我們將在第11章介紹更多細節(jié))。你可以通過一個端口或軟件包管理器安裝 samtools (例如,Mac OS X系統(tǒng)的Homebrew或Ubuntu系統(tǒng)的 apt-get
)。 很像Git,Samtools使用子命令來完成的任務。首先,我們需要使用 faidx
子命令索引FASTA文件:
$ samtools faidx Mus_musculus.GRCm38.75.dna.chromosome.8.fa
這個命令將創(chuàng)建一個名為 Mus_musculus.GRCm38.75.dna.chromosome.8.fa.fai 的索引文件。我們在提取子序列時不必擔心這個索引文件的細節(jié)特征- samtools faidx
命令會注意這些細節(jié)的。要訪問特定區(qū)域的子序列,我們使用 samtools faidx <in.fa> <region>
命令,其中 <in.fa> 是FASTA文件(你剛剛索引的),<region> 的格式為 chromosome: start-end
(染色體:起始位置-終止位置)。例如:
$ samtools faidx Mus_musculus.GRCm38.75.dna.chromosome.8.fa 8:123407082-1??23410744
>8:123407082-1??23410744
GAGAAAAGCTCCCTTCTTCTCCAGAGTCCCGTCTACCCTGGCTTGGCGAGGGAAAGGAAC
CAGACATATATCAGAGGCAAGTAACCAAGAAGTCTGGAGGTGTTGAGTTTAGGCATGTCT
[...]
一定要注意染色體語法的差異(UCSC的Chr 8與ENSEMBL的8的格式)。如果 samtools faidx
命令沒有序列返回,這可能就是原因。
samtools faidx
命令允許一次訪問多個區(qū)域,因此我們可以這樣做:
$ samtools faidx Mus_musculus.GRCm38.75.dna.chromosome.8.fa \
8:123407082-1??23410744 8:123518835-123536649
>8:123407082-1??23410744
GAGAAAAGCTCCCTTCTTCTCCAGAGTCCCGTCTACCCTGGCTTGGCGAGGGAAAGGAAC
CAGACATATATCAGAGGCAAGTAACCAAGAAGTCTGGAGGTGTTGAGTTTAGGCATGTCT
[...]
>8:123518835-123536649
TCTCGCGAGGATTTGAGAACCAGCACGGGATCTAGTCGGAGTTGCCAGGAGACCGCGCAG
CCTCCTCTGACCAGCGCCCATCCCGGATTAGTGGAAGTGCTGGACTGCTGGCACCATGGT
[...]
什么讓訪問索引文件更快?
在第3章(見45頁“全能UNIX流程:速度與美貌共存”), 我們討論了為什么從磁盤讀取和寫入數據相比存儲在內存中的數據非常緩慢。我們可以通過使用指向文件中某些區(qū)塊位置的索引避免不必要地從磁盤中讀取整個文件。在我們使用的FASTA文件的情況下,索引基本上存儲量每個序列在文件中的起始位置(以及其他必要的信息)。
當我們查找像8號染色體(123,407,082-123,410,744)這樣的區(qū)域時,samtools faidx
命令使用索引文件中的信息快速計算那些堿基在文件中的具體位置。然后,使用一個稱為文件搜索的操作,程序跳轉到文件中的精確位置(稱為平移),并開始讀取序列。擁有預先計算的文件偏移量和跳轉到這些確切位置的能力是快速訪問索引文件特定區(qū)域的原因。