這篇文章很長,超過1萬字,是本系列中最重要的一篇,因為我并非只是在簡單地告訴大家?guī)讞l硬邦邦的操作命令。對于新手而言不建議碎片時間閱讀,對于有一定經驗的老手來說,相信依然可以有所收獲。在開始之前,我想先說一句:流程的具體形式其實是次要的,WGS本質上只是一個技術手段,重要的是,我們要明白自己所要解決的問題是什么,所希望獲取的結果是什么,然后再選擇合適的技術。這是許多人經常忽視的一個重要問題。
好了,以下進入正文。
這是WGS數據分析的流程圖。流程的目的是準確檢測出每個樣本(這里特指人)基因組中的變異集合,也就是人與人之間存在差異的那些DNA序列。我把整個分析過程按照它們實際要完成的功能,將其分成了三個大的模塊:
- 原始數據質控
- 數據預處理
- 變異檢測
這或許和很多人看到的WGS分析流程,在結構梳理上有些差異(比如GATK的最佳實踐),但過程中的各個步驟和所要完成的事情是一模一樣的。
0.準備階段
在開始之前,我們需要做一些準備工作,主要是部署好相關的軟件和工具。我們在這個WGS數據分析過程中用到的所有軟件都是開源的,它們的代碼全部都能夠在github上找到,具體如下:
- BWA(Burrow-Wheeler Aligner): 這是最權威,使用最廣的NGS數據比對軟件,目前已經更新到0.7.16版本;
- Samtools: 是一個專門用于處理比對數據的工具,由BWA的作者(lh3)所編寫;
- Picard: 它是目前最著名的組學研究中心-Broad研究所開發(fā)的一款強大的NGS數據處理工具,功能方面和Samtools有些重疊,但更多的是互補,它是由java編寫的,我們直接下載最新的.jar包就行了。
- GATK: 同樣是Broad研究所開發(fā)的,是目前業(yè)內最權威、使用最廣的基因數據變異檢測工具。值得注意的是,目前GATK有3.x和4.x兩個不同的版本,代碼在github上也是分開的。4.x是今年新推出的,在核心算法層面并沒太多的修改,但使用了新的設計模式,做了很多功能的整合,是更適合于大規(guī)模集群和云運算的版本,后續(xù)GATK團隊也將主要維護4.x的版本,而且它的代碼是100%開源的,這和3.x只有部分開源的情況不同。看得出GATK今年的這次升級是為了應對接下來越來越多的大規(guī)模人群測序數據而做出的改變,但現階段4.x版本還不穩(wěn)定,真正在使用的人和機構其實也還不多。短期來看,3.x版本還將在業(yè)內繼續(xù)使用一段時間;其次,3.x對于絕大分部的分析需求來說是完全足夠的。我們在這里也以GATK3.8(最新版本)作為流程的重要工具進行分析流程的構建。
事實上,對于構造WGS分析流程來說,以上這個四個工具就完全足夠了。它們的安裝都非常簡單,除了BWA和Samtools由C編寫的,安裝時需要進行編譯之外,另外兩個只要保證系統(tǒng)中的java是1.8.x版本及以上的,那么直接下載jar包就可以使用了。操作系統(tǒng)方面推薦linux(集群)或者Mac OS。
1.原始數據質控
數據的質控,由于我已經在上一節(jié)的文章中講的比較詳細了,因此在本篇中就不再進行詳細的討論了。而且質控的處理方法都是比較一致的,基本不需要為特定的分析做定制化的改動,因此,我們可以把它作為WGS主流程之外的一環(huán)。但還是再強調一下,數據質控的地位同樣重要,不然我也不必專門為其單獨寫一篇完整的文章。
2.數據預處理
序列比對
先問一個問題:為什么需要比對?
我們已經知道NGS測序下來的短序列(read)存儲于FASTQ文件里面。雖然它們原本都來自于有序的基因組,但在經過DNA建庫和測序之后,文件中不同read之間的前后順序關系就已經全部丟失了。因此,FASTQ文件中緊挨著的兩條read之間沒有任何位置關系,它們都是隨機來自于原本基因組中某個位置的短序列而已。
因此,我們需要先把這一大堆的短序列捋順,一個個去跟該物種的 參考基因組【注】比較,找到每一條read在參考基因組上的位置,然后按順序排列好,這個過程就稱為測序數據的比對。這 也是核心流程真正意義上的第一步,只有完成了這個序列比對我們才有下一步的數據分析。
【注】參考基因組:指該物種的基因組序列,是已經組裝成的完整基因組序列,常作為該物種的標準參照物,比如人類基因組參考序列,fasta格式。
序列比對本質上是一個尋找最大公共子字符串的過程。大家如果有學過生物信息學的話,應該或多或少知道BLAST,它使用的是動態(tài)規(guī)劃的算法來尋找這樣的子串,但在面對巨量的短序列數據時,類似BLAST這樣的軟件實在太慢了!因此,需要更加有效的數據結構和相應的算法來完成這個搜索定位的任務。
我們這里將用于流程構建的BWA就是其中最優(yōu)秀的一個,它將BW(Burrows-Wheeler)壓縮算法和后綴樹相結合,能夠讓我們以較小的時間和空間代價,獲得準確的序列比對結果。
以下我們就開始流程的搭建。
首先,我們需要為參考基因組的構建索引——這其實是在為參考序列進行Burrows Wheeler變換(wiki: 塊排序壓縮),以便能夠在序列比對的時候進行快速的搜索和定位。
$ bwa index human.fasta
以我們人類的參考基因組(3Gb長度)為例,這個構造過程需要消耗幾個小時的時間(一般3個小時左右)。完成之后,你會看到類似如下幾個以human.fasta為前綴的文件:
.
├── human.fasta.amb
├── human.fasta.ann
├── human.fasta.bwt
├── human.fasta.pac
└── human.fasta.sa
這些就是在比對時真正需要被用到的文件。這一步完成之后,我們就可以將read比對至參考基因組了:
$ bwa mem -t 4 -R '@RG\tID:foo_lane\tPL:illumina\tLB:library\tSM:sample_name' /path/to/human.fasta read_1.fq.gz read_2.fq.gz > sample_name.sam
大伙如果以前沒使用過這個比對工具的話,那么可能不明白上面參數的含義。我們這里調用的是bwa的mem比對模塊,在解釋這樣做之前,我們不妨先看一下bwa mem的官方用法說明,它就一句話:
Usage: bwa mem [options] <idxbase> <in1.fq> [in2.fq]
其中,[options]是一系列可選的參數,暫時不多說。這里的 < idxbase>要輸入的是參考基因組的BW索引文件,我們上面通過bwa index
構建好的那幾個以human.fasta為前綴的文件便是;< in1.fq>和 [in2.fq]輸入的是質控后的fastq文件。但這里輸入的時候為什么會需要兩個fq(in1.fq和in2.fq)呢?我們上面的例子也是有兩個:read_1.fq.gz和read_2.fq.gz。這是因為這是雙末端測序(也稱Pair-End)的情況,那什么是“雙末端測序”呢?這兩個fq之間的關系又是什么?這個我需要簡單解析一下。
我們已經知道NGS是短讀長的測序技術,一次測出來的read的長度都不會太長,那為了盡可能把一個DNA序列片段盡可能多地測出來,既然測一邊不夠,那就測兩邊,于是就有了一種從被測DNA序列兩端各測序一次的模式,這就被稱為雙末端測序(Pair-End Sequencing,簡稱PE測序)。如下圖是Pair-End測序的示意圖,中間灰色的是被測序的DNA序列片段,左邊黃色帶箭頭和右邊藍色帶箭頭的分別是測序出來的read1和read2序列,這里假定它們的長度都是100bp。雖然很多時候Pair-End測序還是無法將整個被測的DNA片段完全測通,但是它依然提供了極其有用的信息,比如,我們知道每一對的read1和read2都來自于同一個DNA片段,read1和read2之間的距離是這個DNA片段的長度,而且read1和read2的方向剛好是相反的(這里排除mate-pair的情形)等,這些信息對于后面的變異檢測等分析來說都是非常有用的。
<center>Pair-End 測序</center>
另外,在read1在fq1文件中位置和read2在fq2文件中的文件中的位置是相同的,而且read ID之間只在末尾有一個'/1'或者'/2'的差別。
<center>read1 ID和read2 ID的差別</center>
既然有雙末端測序,那么與之對應的就有單末端測序(Single End Sequecing,簡稱SE測序),即只測序其中一端。因此,我們在使用bwa比對的時候,實際上,in2.fq是非強制性的(所以用方括號括起來),只有是雙末端測序的數據時才需要添加。
回到上面我們的例子,大伙可以看到我這里除了用法中提到的參數之外,還多了2個額外的參數,分別是:-t,線程數,我們在這里使用4個線程;-R 接的是 Read Group的字符串信息,這是一個非常重要的信息,以@RG開頭,它是用來將比對的read進行分組的。不同的組之間測序過程被認為是相互獨立的,這個信息對于我們后續(xù)對比對數據進行錯誤率分析和Mark duplicate時非常重要。在Read Group中,有如下幾個信息非常重要:
(1) ID,這是Read Group的分組ID,一般設置為測序的lane ID(不同lane之間的測序過程認為是獨立的),下機數據中我們都能看到這個信息的,一般都是包含在fastq的文件名中;
(2) PL,指的是所用的測序平臺,這個信息不要隨便寫!特別是當我們需要使用GATK進行后續(xù)分析的時候,更是如此!這是一個很多新手都容易忽視的一個地方,在GATK中,PL只允許被設置為:ILLUMINA,SLX,SOLEXA,SOLID,454,LS454,COMPLETE,PACBIO,IONTORRENT,CAPILLARY,HELICOS或UNKNOWN這幾個信息。基本上就是目前市場上存在著的測序平臺,當然,如果實在不知道,那么必須設置為UNKNOWN,名字方面不區(qū)分大小寫。如果你在分析的時候這里沒設置正確,那么在后續(xù)使用GATK過程中可能會碰到類似如下的錯誤:
ERROR MESSAGE: The platform (xx) associated with read group GATKSAMReadGroupRecord @RG:xx is not a recognized platform.
這個時候你需要對比對文件的header信息進行重寫,就會稍微比較麻煩。
我們上面的例子用的是PL:illumina
。如果你的數據是CG測序的那么記得不要寫成CG!而要寫COMPLETE
。
(3) SM,樣本ID,同樣非常重要,有時候我們測序的數據比較多的時候,那么可能會分成多個不同的lane分布測出來,這個時候SM名字就是可以用于區(qū)分這些樣本。
(4) LB,測序文庫的名字,這個重要性稍微低一些,主要也是為了協(xié)助區(qū)分不同的group而存在。文庫名字一般可以在下機的fq文件名中找到,如果上面的lane ID足夠用于區(qū)分的話,也可以不用設置LB;
除了以上這四個之外,還可以自定義添加其他的信息,不過如無特殊的需要,對于序列比對而言,這4個就足夠了。這些信息設置好之后,在RG字符串中要用制表符(\t)將它們分開。
最后在我們的例子中,我們將比對的輸出結果直接重定向到一份sample_name.sam文件中,這類文件是BWA比對的標準輸出文件,它的具體格式我會在下一篇文章中進行詳細說明。但SAM文件是文本文件,一般整個文件都非常巨大,因此,為了有效節(jié)省磁盤空間,一般都會用samtools將它轉化為BAM文件(SAM的特殊二進制格式),而且BAM會更加方便于后續(xù)的分析。所以我們上面比對的命令可以和samtools結合并改進為:
$ bwa mem -t 4 -R '@RG\tID:foo_lane\tPL:illumina\tLB:library\tSM:sample_name' /path/to/human.fasta read_1.fq.gz read_2.fq.gz | samtools view -S -b - > sample_name.bam
我們通過管道(“|”)把比對的輸出如同引導水流一樣導流給samtools去處理,上面samtools view
的-b參數指的就是輸出為BAM文件,這里需要注意的地方是-b后面的'-',它代表就是上面管道引流過來的數據,經過samtools轉換之后我們再重定向為sample_name.bam。
關于BWA的其他參數,我這里不打算對其進行一一解釋,在絕大多數情況下,采用默認是合適的做法。
[Tips] BWA MEM比對模塊是有一定適用范圍的:它是專門為長read比對設計的,目的是為了解決,第三代測序技術這種能夠產生長達幾十kb甚至幾Mbp的read情況。一般只有當read長度≥70bp的時候,才推薦使用,如果比這個要小,建議使用BWA ALN模塊。
排序
以上,我們就完成了read比對的步驟。接下來是排序:
排序這一步我們也是通過使用samtools來完成的,命令很簡單:
Usage: samtools sort [options...] [in.bam]
但在執(zhí)行之前,我們有必要先搞明白為什么需要排序,為什么BWA比對后輸出的BAM文件是沒順序的!原因就是FASTQ文件里面這些被測序下來的read是隨機分布于基因組上面的,第一步的比對是按照FASTQ文件的順序把read逐一定位到參考基因組上之后,隨即就輸出了,它不會也不可能在這一步里面能夠自動識別比對位置的先后位置重排比對結果。因此,比對后得到的結果文件中,每一條記錄之間位置的先后順序是亂的,我們后續(xù)去重復等步驟都需要在比對記錄按照順序從小到大排序下來才能進行,所以這才是需要進行排序的原因。對于我們的例子來說,這個排序的命令如下:
$ time samtools sort -@ 4 -m 4G -O bam -o sample_name.sorted.bam sample_name.bam
其中,-@,用于設定排序時的線程數,我們設為4;-m,限制排序時最大的內存消耗,這里設為4GB;-O 指定輸出為bam格式;-o 是輸出文件的名字,這里叫sample_name.sorted.bam。我會比較建議大伙在做類似分析的時候在文件名字將所做的關鍵操作包含進去,因為這樣即使過了很長時間,當你再去看這個文件的時候也能夠立刻知道當時對它做了什么;最后就是輸入文件——sample_name.bam。
【注意】排序后如果發(fā)現新的BAM文件比原來的BAM文件稍微小一些,不用覺得驚訝,這是壓縮算法導致的結果,文件內容是沒有損失的。
去除重復序列(或者標記重復序列)
在排序完成之后我們就可以開始執(zhí)行去除重復(準確來說是 去除PCR重復序列)的步驟了。
首先,我們需要先理解什么是重復序列,它是如何產生的,以及為什么需要去除掉?要回答這幾個問題,我們需要再次理解在建庫和測序時到底發(fā)生了什么。
我們在第1節(jié)中已經知道,在NGS測序之前都需要先構建測序文庫:通過物理(超聲)打斷或者化學試劑(酶切)切斷原始的DNA序列,然后選擇特定長度范圍的序列去進行PCR擴增并上機測序。
因此,這里重復序列的來源實際上就是由PCR過程中所引入的。因為所謂的PCR擴增就是把原來的一段DNA序列復制多次。可是為什么需要PCR擴增呢?如果沒有擴增不就可以省略這一步了嗎?
情況確實如此,但是很多時候我們構建測序文庫時能用的細胞量并不會非常充足,而且在打斷的步驟中也會引起部分DNA的降解,這兩點會使整體或者局部的DNA濃度過低,這時如果直接從這個溶液中取樣去測序就很可能漏掉原本基因組上的一些DNA片段,導致測序不全。而PCR擴增的作用就是為了把這些微弱的DNA多復制幾倍乃至幾十倍,以便增大它們在溶液中分布的密度,使得能夠在取樣時被獲取到。所以這里大家需要記住一個重點,PCR擴增原本的目的是為了增大微弱DNA序列片段的密度,但由于整個反應都在一個試管中進行,因此其他一些密度并不低的DNA片段也會被同步放大,那么這時在取樣去上機測序的時候,這些DNA片段就很可能會被重復取到相同的幾條去進行測序(下圖為PCR擴增示意圖)。
<center>PCR擴增示意圖:PCR擴增是一個指數擴增的過程,圖中原本只有一段雙鏈DNA序列,在經過3輪PCR后就被擴增成了8段</center>
看到這里,你或許會覺得,那沒必要去除不也應該可以嗎?因為即便擴增了多幾次,不也同樣還是原來的那一段DNA嗎?直接用來分析對結果也不會有影響啊!難道不是嗎?
會有影響,而且有時影響會很大!最直接的后果就是同時增大了變異檢測結果的假陰和假陽率。主要有幾個原因:
- DNA在打斷的那一步會發(fā)生一些損失,主要表現是會引發(fā)一些堿基發(fā)生顛換變換(嘌呤-變嘧啶或者嘧啶變嘌呤),帶來假的變異。PCR過程會擴大這個信號,導致最后的檢測結果中混入了假的結果;
- PCR反應過程中也會帶來新的堿基錯誤。發(fā)生在前幾輪的PCR擴增發(fā)生的錯誤會在后續(xù)的PCR過程中擴大,同樣帶來假的變異;
- 對于真實的變異,PCR反應可能會對包含某一個堿基的DNA模版擴增更加劇烈(這個現象稱為PCR Bias)。如果反應體系是對含有reference allele的模板擴增偏向強烈,那么變異堿基的信息會變小,從而會導致假陰。
PCR對真實的變異檢測和個體的基因型判斷都有不好的影響。GATK、Samtools、Platpus等這種利用貝葉斯原理的變異檢測算法都是認為所用的序列數據都不是重復序列(即將它們和其他序列一視同仁地進行變異的判斷,所以帶來誤導),因此必須要進行標記(去除)或者使用PCR-Free的測序方案(這個方案目前正變得越來越流行,特別是對于RNA-Seq來說尤為重要,現在著名的基因組學研究所——Broad Institute,基本都是使用PCR-Free的測序方案)。
那么具體是如何做到去除這些PCR重復序列的呢?我們可以拋開任何工具,仔細想想,既然PCR擴增是把同一段DNA序列復制出很多份,那么這些序列在經過比對之后它們一定會定位到基因組上相同的位置,比對的信息看起來也將是一樣的!于是,我們就可以根據這個特點找到這些重復序列了!
事實上,現有的工具包括Samtools和Picard中去除重復序列的算法也的確是這么做的。不同的地方在于,samtools的rmdup是直接將這些重復序列從比對BAM文件中刪除掉,而Picard的MarkDuplicates默認情況則只是在BAM的FLAG信息中標記出來,而不是刪除,因此這些重復序列依然會被留在文件中,只是我們可以在變異檢測的時候識別到它們,并進行忽略。
考慮到盡可能和現在主流的做法一致(但我并不是說主流的做法就一定是對的,要分情況看待,只是主流的做法容易被做成生產流程而已),我們這里也用Picard來完成這個事情:
java -jar picard.jar MarkDuplicates \
I=sample_name.sorted.bam \
O=sample_name.sorted.markdup.bam \
M=sample_name.markdup_metrics.txt
這里只把重復序列在輸出的新結果中標記出來,但不刪除。如果我們非要把這些序列完全刪除的話可以這樣做:
java -jar picard.jar MarkDuplicates \
REMOVE_DUPLICATES=true \
I=sample_name.sorted.bam \
O=sample_name.sorted.markdup.bam \
M=sample_name.markdup_metrics.txt
把參數REMOVE_DUPLICATES
設置為ture,那么重復序列就被刪除掉,不會在結果文件中留存。我比較建議使用第一種做法,只是標記出來,并留存這些序列,以便在你需要的時候還可以對其做分析。
這一步完成之后,我們需要為sample_name.sorted.markdup.bam創(chuàng)建索引文件,它的作用能夠讓我們可以隨機訪問這個文件中的任意位置,而且后面的“局部重比對”步驟也要求這個BAM文件一定要有索引,命令如下:
$ samtools index sample_name.sorted.markdup.bam
完成之后,會生成一份sample_name.sorted.markdup.bam.bai文件,這就是上面這份BAM的index。
局部重比對
接下來是局部區(qū)域重比對,通常也叫Indel局部區(qū)域重比對。有時在進行這一步驟之前還有一個merge的操作,將同個樣本的所有比對結果合并成唯一一個大的BAM文件【注】,merge的例子如下:
$ samtools merge <out.bam> <in1.bam> [<in2.bam> ... <inN.bam>]
【注意】之所以會有這種情況,是因為有些樣本測得非常深,其測序結果需要經過多次測序(或者分布在多個不同的測序lane中)才全部獲得,這個時候我們一般會先分別進行比對并去除重復序列后再使用samtools進行合并。
局部重比對的目的是將BWA比對過程中所發(fā)現有 潛在序列插入或者序列刪除(insertion和deletion,簡稱Indel)的區(qū)域進行重新校正。這個過程往往還會把一些已知的Indel區(qū)域一并作為重比對的區(qū)域,但為什么需要進行這個校正呢?
其根本原因來自于參考基因組的序列特點和BWA這類比對算法本身,注意這里不是針對BWA,而是針對所有的這類比對算法,包括bowtie等。這類在全局搜索最優(yōu)匹配的算法在存在Indel的區(qū)域及其附近的比對情況往往不是很準確,特別是當一些存在長Indel、重復性序列的區(qū)域或者存在長串單一堿基(比如,一長串的TTTT或者AAAAA等)的區(qū)域中更是如此。
另一個重要的原因是在這些比對算法中,對堿基錯配和開gap的容忍度是不同的。具體體現在罰分矩陣的偏向上,例如,在read比對時,如果發(fā)現堿基錯配和開gap都可以的話,它們會更偏向于錯配。但是這種偏向錯配的方式,有時候卻還會反過來引起錯誤的開gap!這就會導致基因組上原本應該是一個長度比較大的Indel的地方,被錯誤地切割成多個錯配和短indel的混合集,這必然會讓我們檢測到很多錯誤的變異。而且,這種情況還會隨著所比對的read長度的增長(比如三代測序的Read,通常都有幾十kbp)而變得越加嚴重。
因此,我們需要有一種算法來對這些區(qū)域進行局部的序列重比對。這個算法通常就是大名鼎鼎的Smith-Waterman算法,它非常適合于這類場景,可以極其有效地實現對全局比對結果的校正和調整,最大程度低地降低由全局比對算法的不足而帶來的錯誤。而且GATK的局部重比對模塊,除了應用這個算法之外,還會對這個區(qū)域中的read進行一次局部組裝,把它們連接成為長度更大的序列,這樣能夠更進一步提高局部重比對的準確性。
下圖給大家展示一個序列重比對之前和之后的結果,其中灰色的橫條指的是read,空白黑線指的是deletion,有顏色的堿基指的是錯配堿基。
<center>Indel局部重比對的前后的對比</center>
相信大家都可以明顯地看到在序列重比對之前,在這個區(qū)域的比對數據是多么的糟糕,如果就這樣進行變異檢測,那么一定會得到很多假的結果。而在經過局部重比對之后,這個區(qū)域就變得非常清晰而分明,它原本發(fā)生的就只是一個比較長的序列刪除(deletion)事件,但在原始的比對結果中卻被錯誤地用堿基錯配和短的Indel所代替。
說到這里,那么具體該怎么做呢?我們的WGS分析流程從這個步驟開始就需要用到GATK (GenomeAnalysisTK.jar)了,我們的執(zhí)行命令如下:
java -jar /path/to/GenomeAnalysisTK.jar \
-T RealignerTargetCreator \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.bam \
-known /path/to/gatk/bundle/1000G_phase1.indels.b37.vcf \
-known /path/to/gatk/bundle/Mills_and_1000G_gold_standard.indels.b37.vcf \
-o sample_name.IndelRealigner.intervals
java -jar /path/to/GenomeAnalysisTK.jar \
-T IndelRealigner \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.bam \
-known /path/to/gatk/bundle/1000G_phase1.indels.b37.vcf \
-known /path/to/gatk/bundle/Mills_and_1000G_gold_standard.indels.b37.vcf \
-o sample_name.sorted.markdup.realign.bam \
--targetIntervals sample_name.IndelRealigner.intervals
這里包含了兩個步驟:
- 第一步,RealignerTargetCreator ,目的是定位出所有需要進行序列重比對的目標區(qū)域(如下圖);
- 第二步,IndelRealigner,對所有在第一步中找到的目標區(qū)域運用算法進行序列重比對,最后得到捋順了的新結果。
<center>IndelRealigner.intervals文件內容示例</center>
以上這兩個步驟是缺一不可的,順序也是固定的。而且,需要指出的是,這里的-R參數輸入的human.fasta不是BWA比對中的索引文件前綴,而是參考基因組序列(FASTA格式)文件,下同。
另外,在重比對步驟中,我們還看到了兩個陌生的VCF文件,分別是:1000G_phase1.indels.b37.vcf和Mills_and_1000G_gold_standard.indels.b37.vcf。這兩個文件來自于千人基因組和Mills項目,里面記錄了那些項目中檢測到的人群Indel區(qū)域。我上面其實也提到了,候選的重比對區(qū)除了要在樣本自身的比對結果中尋找之外,還應該把人群中已知的Indel區(qū)域也包含進來,而這兩個是我們在重比對過程中最常用到的。這些文件你可以很方便地在GATK bundle ftp中下載,注意一定要選擇和你的參考基因組對應的版本,我們這里用的是b37版本。
<center>GATK bundle</center>
那么既然Indel局部重比對這么好,這么重要,似乎看起來在任何情況下都應該是必須的。然鵝,我的回答是否定的!驚訝嗎!
但否定是有前提的!那就是我們后面的變異檢測必須是使用GATK,而且必須使用GATK的HaplotypeCaller模塊,僅當這個時候才可以減少這個Indel局部重比對的步驟。原因是GATK的HaplotypeCaller中,會對潛在的變異區(qū)域進行相同的局部重比對!但是其它的變異檢測工具或者GATK的其它模塊就沒有這么干了!所以切記!
重新校正堿基質量值(BQSR)
在WGS分析中,變異檢測是一個極度依賴測序堿基質量值的步驟。因為這個質量值是衡量我們測序出來的這個堿基到底有多正確的重要(甚至是唯一)指標。它來自于測序圖像數據的base calling。因此,基本上是由測序儀和測序系統(tǒng)來決定的。但不幸的是,影響這個值準確性的系統(tǒng)性因素有很多,包括物理和化學等對測序反應的影響,甚至連儀器本身和周圍環(huán)境都是其重要的影響因素。當把所有這些東西綜合在一起之后,往往會發(fā)現計算出來的堿基質量值要么高于真實結果,要么低于真實結果。那么,我們到底該如何才能獲得符合真實情況的堿基質量值?
BQSR(Base Quality Score Recalibration)這個步驟就是為此而存在的,這一步同樣非常重要。它主要是通過機器學習的方法構建測序堿基的錯誤率模型,然后對這些堿基的質量值進行相應的調整。
<center>BQSR質量校正對比</center>
圖中,橫軸(Reported quality score)是測序結果在Base calling之后報告出來的質量值,也就是我們在FASTQ文件中看到的那些;縱軸(Empirical quality score)代表的是“真實情況的質量值”。
但是且慢,這個“真實情況的質量值”是怎么來的?因為實際上我們并沒有辦法直接測得它們啊!沒錯,確實沒辦法直接測量到,但是我們可以通過統(tǒng)計學的技巧獲得極其接近的分布結果(因此我加了引號)。試想一下,如果我們在看到某一個堿基報告的質量值是20時,那么它的預期錯誤率是1%,反過來想,就等于是說如果有100個質量值都是20的堿基,那么從統(tǒng)計上講它們中將只有1個是錯的!做了這個等效變換之后,我們的問題就可以轉變成為尋找錯誤堿基的數量了。
這時問題就簡單多了。我們知道人與人之間的差異其實是很小的,那么在一個群體中發(fā)現的已知變異,在某個人身上也很可能是同樣存在的。因此,這個時候我們可以對比對結果進行直接分析,首先排除掉所有的已知變異位點,然后計算每個(報告出來的)質量值下面有多少個堿基在比對之后與參考基因組上的堿基是不同的,這些不同堿基就被我們認為是錯誤的堿基,它們的數目比例反映的就是真實的堿基錯誤率,換算成Phred score(Phred score的定義可以參考第2節(jié)的相關內容)之后,就是縱軸的Empirical quality score了。
上面‘BQSR質量校正對比’的圖中左邊是原始質量值與真實質量值的比較,在這個圖的例子中我們可以發(fā)現,base calling給出的質量值并沒有正確地反映真實的錯誤率情況,測序報告出來的堿基質量值大部分被高估了,換句話說,就是錯誤率被低估了。
在我們的流程中,BQSR的具體執(zhí)行命令如下:
java -jar /path/to/GenomeAnalysisTK.jar \
-T BaseRecalibrator \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.realign.bam \
--knownSites /path/to/gatk/bundle/1000G_phase1.indels.b37.vcf \
--knownSites /path/to/gatk/bundle/Mills_and_1000G_gold_standard.indels.b37.vcf \
--knownSites /path/to/gatk/bundle/dbsnp_138.b37.vcf \
-o sample_name.recal_data.table
java -jar /path/to/GenomeAnalysisTK.jar \
-T PrintReads \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.realign.bam \
--BQSR sample_name.recal_data.table \
-o sample_name.sorted.markdup.realign.BQSR.bam
這里同樣包含了兩個步驟:
- 第一步,BaseRecalibrator,這里計算出了所有需要進行重校正的read和特征值,然后把這些信息輸出為一份校準表文件(sample_name.recal_data.table)
- 第二步,PrintReads,這一步利用第一步得到的校準表文件(sample_name.recal_data.table)重新調整原來BAM文件中的堿基質量值,并使用這個新的質量值重新輸出一份新的BAM文件。
注意,因為BQSR實際上是為了(盡可能)校正測序過程中的系統(tǒng)性錯誤,因此,在執(zhí)行的時候是按照不同的測序lane或者測序文庫來進行的,這個時候@RG信息(BWA比對時所設置的)就顯得很重要了,算法就是通過@RG中的ID來識別各個獨立的測序過程,這也是我開始強調其重要性的原因。
變異檢測
事實上,這是目前所有WGS數據分析流程的一個目標——獲得樣本準確的變異集合。這里變異檢測的內容一般會包括:SNP、Indel,CNV和SV等,這個流程中我們只做其中最主要的兩個:SNP和Indel。我們這里使用GATK HaplotypeCaller模塊對樣本中的變異進行檢測,它也是目前最適合用于對二倍體基因組進行變異(SNP+Indel)檢測的算法。
HaplotypeCaller和那些直接應用貝葉斯推斷的算法有所不同,它會先推斷群體的單倍體組合情況,計算各個組合的幾率,然后根據這些信息再反推每個樣本的基因型組合。因此它不但特別適合應用到群體的變異檢測中,而且還能夠依據群體的信息更好地計算每個個體的變異數據和它們的基因型組合。
一般來說,在實際的WGS流程中對HaplotypeCaller的應用有兩種做法,差別只在于要不要在中間生成一個gVCF:
(1)直接進行HaplotypeCaller,這適合于單樣本,或者那種固定樣本數量的情況,也就是執(zhí)行一次HaplotypeCaller之后就老死不相往來了。否則你會碰到僅僅只是增加一個樣本就得重新運行這個HaplotypeCaller的坑爹情況(即,N+1難題),而這個時候算法需要重新去讀取所有人的BAM文件,這將會是一個很費時間的痛苦過程;
(2)每個樣本先各自生成gVCF,然后再進行群體joint-genotype。這其實就是GATK團隊為了解決(1)中的N+1難題而設計出來的模式。gVCF全稱是genome VCF,是每個樣本用于變異檢測的中間文件,格式類似于VCF,它把joint-genotype過程中所需的所有信息都記錄在這里面,文件無論是大小還是數據量都遠遠小于原來的BAM文件。這樣一旦新增加樣本也不需要再重新去讀取所有人的BAM文件了,只需為新樣本生成一份gVCF,然后重新執(zhí)行這個joint-genotype就行了。
我們先以第一種(直接HaplotypeCaller)做法為例子:
java -jar /path/to/GenomeAnalysisTK.jar \
-T HaplotypeCaller \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.realign.BQSR.bam \
-D /path/to/gatk/bundle/dbsnp_138.b37.vcf \
-stand_call_conf 50 \
-A QualByDepth \
-A RMSMappingQuality \
-A MappingQualityRankSumTest \
-A ReadPosRankSumTest \
-A FisherStrand \
-A StrandOddsRatio \
-A Coverage \
-o sample_name.HC.vcf
這里我特別提一下-D參數輸入的dbSNP同樣可以再GATK bundle目錄中找到,這份文件匯集的是目前幾乎所有的公開人群變異數據集。另外,由于我們的例子只有一個樣本因此只輸入一個BAM文件就可以了,如果有多個樣本那么可以繼續(xù)用-I參數輸入:
java -jar GenomeAnalysisTK.jar \
-T HaplotypeCaller \
-R reference.fasta \
-I sample1.bam [-I sample2.bam ...] \
...
以上的命令是直接對全基因組做變異檢測,這個過程會消耗很長的時間,通常需要幾十個小時甚至幾天。
然而,基因組上各個不同的染色體之間其實是可以理解為相互獨立的(結構性變異除外),也就是說,為了提高效率我們可以按照染色體一條條來獨立執(zhí)行這個步驟,最后再把結果合并起來就好了,這樣的話就能夠節(jié)省很多的時間。下面我給出一個按照染色體區(qū)分的例子:
java -jar /path/to/GenomeAnalysisTK.jar \
-T HaplotypeCaller \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.realign.BQSR.bam \
-D /path/to/gatk/bundle/dbsnp_138.b37.vcf \
-L 1 \
-stand_call_conf 50 \
-A QualByDepth \
-A RMSMappingQuality \
-A MappingQualityRankSumTest \
-A ReadPosRankSumTest \
-A FisherStrand \
-A StrandOddsRatio \
-A Coverage \
-o sample_name.HC.1.vcf
注意到了嗎?其它參數都沒任何改變,就只增加了一個 -L 參數,通過這個參數我們可以指定特定的染色體(或者基因組區(qū)域)!我們這里指定的是 1 號染色體,有些地方會寫成chr1,具體看human.fasta中如何命名,與其保持一致即可。其他染色體的做法也是如此,就不再舉例了。最后合并:
java -jar /path/to/GenomeAnalysisTK.jar \
-T CombineVariants \
-R /path/to/human.fasta \
--genotypemergeoption UNSORTED \
--variant sample_name.HC.1.vcf \
--variant sample_name.HC.2.vcf \
...
--variant sample_name.HC.MT.vcf \
-o sample_name.HC.vcf
第二種,先產生gVCF,最后再joint-genotype的做法:
java -jar /path/to/GenomeAnalysisTK.jar \
-T HaplotypeCaller \
-R /path/to/human.fasta \
-I sample_name.sorted.markdup.realign.BQSR.bam \
--emitRefConfidence GVCF \
-o sample_name.g.vcf
#調用GenotypeGVCFs完成變異calling
java -jar /path/to/GenomeAnalysisTK.jar \
-T GenotypeGVCFs \
-R /path/to/human.fasta \
--variant sample_name.g.vcf \
-o sample_name.HC.vcf
其實,就是加了--emitRefConfidence GVCF的參數。而且,假如嫌慢,同樣可以按照染色體或者區(qū)域去產生一個樣本的gVCF,然后在GenotypeGVCFs中把它們全部作為輸入文件完成變異calling。也許你會擔心同個樣本被分成多份gVCF之后,是否會被當作不同的多個樣本?回答是不會!因為生成gVCF文件的過程中,GATK會根據@RG信息中的SM(也就是sample name)來判斷這些gVCF是否來自同一個樣本,如果名字相同,那么就會被認為是同一個樣本,不會產生多樣本問題。
變異檢測質控和過濾(VQSR)
這是我們這個流程中最后的一步了。在獲得了原始的變異檢測結果之后,我們還需要做的就是質控和過濾。這一步或多或少都有著一些個性化的要求,我暫時就不做太多解釋吧(一旦解釋恐怕同樣是一篇萬字長文)。只用一句話來概括,VQSR是通過構建GMM模型對好和壞的變異進行區(qū)分,從而實現對變異的質控,具體的原理暫時不展開了。
下面就直接給出例子吧:
## SNP Recalibrator
java -jar /path/to/GenomeAnalysisTK.jar \
-T VariantRecalibrator \
-R reference.fasta \
-input sample_name.HC.vcf \
-resource:hapmap,known=false,training=true,truth=true,prior=15.0 /path/to/gatk/bundle/hapmap_3.3.b37.vcf \
-resource:omini,known=false,training=true,truth=false,prior=12.0 /path/to/gatk/bundle/1000G_omni2.5.b37.vcf \
-resource:1000G,known=false,training=true,truth=false,prior=10.0 /path/to/gatk/bundle/1000G_phase1.snps.high_confidence.b37.vcf \
-resource:dbsnp,known=true,training=false,truth=false,prior=6.0 /path/to/gatk/bundle/dbsnp_138.b37.vcf \
-an QD -an MQ -an MQRankSum -an ReadPosRankSum -an FS -an SOR -an DP \
-mode SNP \
-recalFile sample_name.HC.snps.recal \
-tranchesFile sample_name.HC.snps.tranches \
-rscriptFile sample_name.HC.snps.plots.R
java -jar /path/to/GenomeAnalysisTK.jar -T ApplyRecalibration \
-R human_g1k_v37.fasta \
-input sample_name.HC.vcf \
--ts_filter_level 99.5 \
-tranchesFile sample_name.HC.snps.tranches \
-recalFile sample_name.HC.snps.recal \
-mode SNP \
-o sample_name.HC.snps.VQSR.vcf
## Indel Recalibrator
java -jar /path/to/GenomeAnalysisTK.jar -T VariantRecalibrator \
-R human_g1k_v37.fasta \
-input sample_name.HC.snps.VQSR.vcf \
-resource:mills,known=true,training=true,truth=true,prior=12.0 /path/to/gatk/bundle/Mills_and_1000G_gold_standard.indels.b37.vcf \
-an QD -an DP -an FS -an SOR -an ReadPosRankSum -an MQRankSum \
-mode INDEL \
-recalFile sample_name.HC.snps.indels.recal \
-tranchesFile sample_name.HC.snps.indels.tranches \
-rscriptFile sample_name.HC.snps.indels.plots.R
java -jar /path/to/GenomeAnalysisTK.jar -T ApplyRecalibration \
-R human_g1k_v37.fasta\
-input sample_name.HC.snps.VQSR.vcf \
--ts_filter_level 99.0 \
-tranchesFile sample_name.HC.snps.indels.tranches \
-recalFile sample_name.HC.snps.indels.recal \
-mode INDEL \
-o sample_name.HC.snps.indels.VQSR.vcf
最后,sample_name.HC.snps.indels.VQSR.vcf
便是我們最終的變異檢測結果。對于人類而言,一般來說,每個人最后檢測到的變異數據大概在400萬左右(包括SNP和Indel)。
這篇文章已經很長了,在變異檢測的這個過程中GATK應用了很多重要的算法,包括如何構建模型、如何進行局部組裝和比對、如何應用貝葉斯、PariHMM、GMM、參數訓練、特征選擇等等,這些只能留在后面介紹GATK的專題文章中再進行展開了。
小結
在這里,整篇文章就結束了。如你所見,文章非常長,這里基本包含了WGS最佳實踐中的所有內容,但其實我想說的還遠不止如此(包括CNV和SV的檢測),只是暫時只能作罷了,否則恐怕就沒人愿意看下去了,呵呵。在這個WGS主流程的構建過程中,我并非只是硬邦邦地告訴大家?guī)讞l簡單的命令就了事了,因為我認為那種做法要么是極其不負責任的,要么就是作者并非真的懂。而且如果都覺得只要懂得幾條命令就可以了的話,那么我們就活該被機器和人工智能所取替,它們一定會操作得更好更高效。我想掌握工具和技術的目的是為了能夠更好地發(fā)現并解決問題(包括科研和生產),所有的數據分析流程本質上是要服務于我們所要解決的問題的。
畢竟工具是死的,人是活的,需求總是會變的。理解我們所要處理的問題的本質,選擇合適的工具,而不是反過來被工具所束縛,這一點很重要。個人的能力不能只是會跑一個流程,或者只是會創(chuàng)建流程,因為那都是一個“術”的問題,我覺得我們真正要去掌握的應該是如何分析數據的能力,如何發(fā)現問題和解決數據問題等的能力。
歡迎關注我的公眾號:堿基礦工(helixminer),更及時了解更多信息