人類的參考基因組大約含有31.6億個鹼基對,100萬條150bp讀長的read假若一一對應得通過Needleman-Wunsch算法(https://www.jianshu.com/p/85465f32273a)做雙序列比對,時間真讓人等不起。因此,大多數mapping軟件將“序列回帖”分成 seed alignment 和 seed extension兩個步驟。
seed alignment:首先通過截取read中的短序列片段(稱爲 seed)與參考基因組比對,來找到reference的index;
seed extension:然後通過reference的index將附近的序列與seed對應read做雙序列比對 (Needleman-Wunsch算法或Smith-Waterman算法)
這篇文章就記錄一下seed alignment 的BWT算法,不過,BWT算法原本用於數據壓縮,而它的 壓縮 和 解壓縮 的過程也可以直接類比到,參考基因組做索引 和 seed alignment 的雙序列比對。
1. BWT算法做數據壓縮的原理
1.1 序列ababc
數據壓縮
以下步驟與圖1一一對應:
舉例:壓縮字符串ababc
輸入字符串 ababc
第一步,添加標記ababc$
第二步,ababc$
“循環轉移”(序列最後一個字母“依次”移動到最前端)
第三步,將“循環轉移”獲得的矩陣按照 第一列首字母 排序獲得M數組
第四步,取出M數組的第一列爲 F列;M數組的最後一列爲 L列
數據壓縮:做到這一步之後,便可以直接將 L列 以c,$,b,2a,b
的形式存儲,實現了字符串ababc
的數據壓縮(不過這個舉例裏壓縮率並不高😓)
1.2 數據解壓縮
數據解壓縮,也就是從L列內容還原原始字符串。其中勢必用到了M數組中的兩個特殊列 F列和L列,還有他們之間的相互關係。
BWT算法的三項原則:
- 同一行中,“L列字母”爲“F列字母”的前一個字母
- L列字母排序就可以得到F列(因爲F列和L列其實元素是相同的)
- 單字母的相對位置不變(F列的第n個a對應L列的第n個a,這個結論可以自己推導)
如何通過F列和L列還原原始序列?也就是解壓縮過程:
$
爲序列的結尾標誌,因此從$
開始反向推導原始序列(“L列字母”爲“F列字母”的前一個字母;F列和L列單字母的相對位置不變)
- 從F列的
$
開始 - 根據第一行,
$
的前一個字母爲 c - L列第一行的
c
對應F列的第六行的c
(均爲兩列中的第1個c
) - 根據第六行,
c
的前一個字母爲 b - L列第六行的
b
對應F列的第五行的b
(均爲兩列中的第2個b
) - 根據第五行,
b
的前一個字母爲 a - L列第五行的
a
對應F列的第三行的a
(均爲兩列中的第2個a
) - 根據第三行,
a
的前一個字母爲 b - L列第三行的
b
對應F列的第四行b
(均爲兩列中的第1個b
) - 根據第四行,
b
的前一個字母爲 a - L列第四行的
a
對應F列的第二行a
(均爲兩列中的第1個a
) - 根據第二行,
a
的前一個字母爲 $(已經找回到起點$
,說明序列已經完全恢復)
以上步驟中,黑粗體描述的字母 從下向上 排列爲:ababc
,即達到了恢復原始序列的目的。
BWT用於數據壓縮,是因爲L列(
c,$,b,a,a,b
)可以直接儲存爲(c,$,b,2a,b
)。這種壓縮重複鹼基的形式,對於存在“高重複區域”的基因組序列而言,可以極大的減少存儲空間。同時藉助F列做位置參考,很容易能找回原始序列。
2. BWT算法做序列比對
我們如何用BWT的算法做鹼基序列比對?實際上,以上提到 數據壓縮和解壓縮 的過程就是我們做序列比對的過程。
1)我們建立參考基因組的索引,其實便是建立refercen序列的L列和它相對位置的index(體現在👆便是ababc
獲得L列的過程,也就是 數據壓縮 的過程);
2)我們將測序得到的reads與參考基因組比對,其實便是查找reads對應參考基因組的位置,並觀察reads序列是否可以還原出對應位置的鹼基序列(體現在👆便是由L列排序獲得F列,然後以F列配合做指引,從最後一個字母出發做 數據解壓縮 )
舉例:abab
是否爲ababc
的子序列?我們看BWT算法是如何判斷的
比對的過程是從後向前的"倒序比對",因此
abab
比對順序應該爲baba
,從F列對應字母開始向前追溯。因爲F列有2個b
,因此要做兩次比對:第一次:
- 從F列的第1個 b 開始
- 根據第四行,
b
的前一個字母爲 a - L列第四行的
a
對應F列的第二行的a
- 根據第二行,
a
的前一個字母爲 $
直接終止比對過程,比對到的序列爲ab
第二次:
- 從F列的第2個 b 開始
- 根據第五行,
b
的前一個字母爲 a - L列第五行的
a
對應F列的第三行的a
- 根據第三行,
a
的前一個字母爲 b - L列第三行的
b
對應F列的第四行的b
- 根據第四行,
b
的前一個字母爲 a
至此已經比對到序列abab
因此軟件判斷abab
爲ababc
的子序列
PS:bowtie 裏的FM-index 的實現方式:
在 BWT 算法的基礎上增加以下兩個問題的解決方法⚠️ 1. 計算機如何判斷參考序列(即L列)中 鹼基的相對位置?
例如:當前seed比對到參考序列的A
在參考序列(即L列) “所有A
鹼基”中的排序號碼是多少?
bowtie的做法:每隔128行設置一個check point(A:233;G:231;C:256;T:271),通過距離該鹼基最近的check point獲得鹼基的相對位置。⚠️ 2. 計算機如何判斷reads在參考基因組中的位置?
最簡單粗暴的方式是:通過將後綴數組載入內存,添加每個鹼基的位置信息。但是人類基因組的後綴數組約爲12G,完全載入特別耗內存。
bowtie的做法:每32行設置一個sample位置值,因此要確定位置信息,只需要回讀到sample位置區,即可推導read比對到參考基因組的位置信息。(如此便將後綴數組的內存降低爲1/32)
參考鏈接:https://blog.csdn.net/stormlovetao/article/details/7048481
實際的比對過程中,測序得到的reads都被分割成幾十bp的片段,選取其中的部分質量較好的序列作爲seed序列與參考基因組比對(循環如上的 解壓縮 過程),找到reads在基因組上大概的位置(比對上的位置可能會很多,會綜合很多因素:insertion、deletion、mismatch、reads quality等等,爲每一個位置打分,最終取得分最高的位置)。
確定位置之後,取出參考基因組對應位置附近的序列,和reads做雙序列比對。