BLAST 和其他序列搜尋工具

生物序列辨識是生物資訊學不可或缺的一部分。有多種工具可用於此,每種工具都有自己的演算法和方法,例如 BLAST(可以說是​​最受歡迎的)、FASTA、HMMER 以及更多。一般來說,這些工具通常會使用您的序列來搜尋潛在匹配的資料庫。隨著已知序列的數量不斷增加(因此潛在匹配的數量不斷增加),解釋結果變得越來越困難,因為可能會有數百甚至數千個潛在匹配。當然,人工解釋這些搜尋結果是不可能的。此外,您通常需要使用多種序列搜尋工具,每種工具都有自己的統計數據、慣例和輸出格式。想像一下,當您需要使用多種搜尋工具處理多個序列時,情況會是多麼令人望而生畏。

我們自己也很清楚這一點,這就是我們在 Biopython 中建立 Bio.SearchIO 子模組的原因。Bio.SearchIO 可讓您以便利的方式從搜尋結果中提取資訊,同時處理不同搜尋工具使用的不同標準和慣例。名稱 SearchIO 是對 BioPerl 同名模組的致敬。

在本章中,我們將介紹 Bio.SearchIO 的主要功能,以展示它能為您做什麼。我們將一路使用兩個流行的搜尋工具:BLAST 和 BLAT。它們僅用於說明目的,您應該能夠輕鬆地將工作流程調整為 Bio.SearchIO 支援的任何其他搜尋工具。我們非常歡迎您使用我們將使用的搜尋輸出檔案。可以在此下載 BLAST 輸出檔案,而 BLAT 輸出檔案可以在此下載,或者包含在 Biopython 原始碼的 Doc/examples/ 資料夾下。這兩個輸出檔案都是使用此序列產生的

>mystery_seq
CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTTTAGAGGG

BLAST 結果是一個 XML 檔案,使用 blastn 針對 NCBI refseq_rna 資料庫產生。對於 BLAT,序列資料庫是 2009 年 2 月的 hg19 人類基因組草案,而輸出格式為 PSL。

我們將從介紹 Bio.SearchIO 物件模型開始。該模型是您的搜尋結果的表示,因此它是 Bio.SearchIO 本身的核心。之後,我們將檢查您可能經常使用的 Bio.SearchIO 中的主要函數。

現在我們都準備好了,讓我們開始第一步:介紹核心物件模型。

SearchIO 物件模型

儘管許多序列搜尋工具的輸出樣式差異很大,但事實證明它們的基本概念是相似的

  • 輸出檔案可能包含一個或多個搜尋查詢的結果。

  • 在每個搜尋查詢中,您會看到來自給定搜尋資料庫的一個或多個命中。

  • 在每個資料庫命中中,您會看到一個或多個區域,其中包含您的查詢序列與資料庫序列之間的實際序列比對。

  • 像 BLAT 或 Exonerate 這樣的一些程式可能會進一步將這些區域分割成多個比對片段(或 BLAT 中的區塊,以及 exonerate 中可能的 exons)。這並不是您總是會看到的,因為像 BLAST 和 HMMER 這樣的程式不會這樣做。

意識到這種普遍性,我們決定將其用作建立 Bio.SearchIO 物件模型的基礎。該物件模型由一個巢狀的 Python 物件層次結構組成,每個物件都代表上面概述的一個概念。這些物件是

  • QueryResult,表示單一搜尋查詢。

  • Hit,表示單一資料庫命中。Hit 物件包含在 QueryResult 中,並且在每個 QueryResult 中都有零個或多個 Hit 物件。

  • HSP(高分對的縮寫),表示查詢序列和命中序列之間顯著比對的區域。HSP 物件包含在 Hit 物件中,並且每個 Hit 都有一個或多個 HSP 物件。

  • HSPFragment,表示查詢序列和命中序列之間的單一連續比對。HSPFragment 物件包含在 HSP 物件中。大多數序列搜尋工具(如 BLAST 和 HMMER)將 HSPHSPFragment 物件統一,因為每個 HSP 只會有單一 HSPFragment。但是,像 BLAT 和 Exonerate 這樣的工具有時會產生包含多個 HSPFragmentHSP。如果現在覺得有點混亂,請別擔心,我們稍後會詳細說明這兩個物件。

這四個物件是您在使用 Bio.SearchIO 時將會互動的物件。它們是使用主要的 Bio.SearchIO 方法之一建立的:readparseindexindex_db。這些方法的詳細資訊將在後續章節中提供。在本節中,我們將僅使用 read 和 parse。這些函數的行為與它們的 Bio.SeqIOBio.AlignIO 對應函數相似

  • read 用於具有單一查詢的搜尋輸出檔案,並返回 QueryResult 物件

  • parse 用於具有多個查詢的搜尋輸出檔案,並返回一個產生 QueryResult 物件的產生器

有了這些基礎知識後,讓我們開始探測每個 Bio.SearchIO 物件,從 QueryResult 開始。

QueryResult

QueryResult 物件表示單一搜尋查詢,並包含零個或多個 Hit 物件。讓我們看看它使用我們擁有的 BLAST 檔案是什麼樣子

>>> from Bio import SearchIO
>>> blast_qresult = SearchIO.read("my_blast.xml", "blast-xml")
>>> print(blast_qresult)
Program: blastn (2.2.27+)
  Query: 42291 (61)
         mystery_seq
 Target: refseq_rna
   Hits: ----  -----  ----------------------------------------------------------
            #  # HSP  ID + description
         ----  -----  ----------------------------------------------------------
            0      1  gi|262205317|ref|NR_030195.1|  Homo sapiens microRNA 52...
            1      1  gi|301171311|ref|NR_035856.1|  Pan troglodytes microRNA...
            2      1  gi|270133242|ref|NR_032573.1|  Macaca mulatta microRNA ...
            3      2  gi|301171322|ref|NR_035857.1|  Pan troglodytes microRNA...
            4      1  gi|301171267|ref|NR_035851.1|  Pan troglodytes microRNA...
            5      2  gi|262205330|ref|NR_030198.1|  Homo sapiens microRNA 52...
            6      1  gi|262205302|ref|NR_030191.1|  Homo sapiens microRNA 51...
            7      1  gi|301171259|ref|NR_035850.1|  Pan troglodytes microRNA...
            8      1  gi|262205451|ref|NR_030222.1|  Homo sapiens microRNA 51...
            9      2  gi|301171447|ref|NR_035871.1|  Pan troglodytes microRNA...
           10      1  gi|301171276|ref|NR_035852.1|  Pan troglodytes microRNA...
           11      1  gi|262205290|ref|NR_030188.1|  Homo sapiens microRNA 51...
           12      1  gi|301171354|ref|NR_035860.1|  Pan troglodytes microRNA...
           13      1  gi|262205281|ref|NR_030186.1|  Homo sapiens microRNA 52...
           14      2  gi|262205298|ref|NR_030190.1|  Homo sapiens microRNA 52...
           15      1  gi|301171394|ref|NR_035865.1|  Pan troglodytes microRNA...
           16      1  gi|262205429|ref|NR_030218.1|  Homo sapiens microRNA 51...
           17      1  gi|262205423|ref|NR_030217.1|  Homo sapiens microRNA 52...
           18      1  gi|301171401|ref|NR_035866.1|  Pan troglodytes microRNA...
           19      1  gi|270133247|ref|NR_032574.1|  Macaca mulatta microRNA ...
           20      1  gi|262205309|ref|NR_030193.1|  Homo sapiens microRNA 52...
           21      2  gi|270132717|ref|NR_032716.1|  Macaca mulatta microRNA ...
           22      2  gi|301171437|ref|NR_035870.1|  Pan troglodytes microRNA...
           23      2  gi|270133306|ref|NR_032587.1|  Macaca mulatta microRNA ...
           24      2  gi|301171428|ref|NR_035869.1|  Pan troglodytes microRNA...
           25      1  gi|301171211|ref|NR_035845.1|  Pan troglodytes microRNA...
           26      2  gi|301171153|ref|NR_035838.1|  Pan troglodytes microRNA...
           27      2  gi|301171146|ref|NR_035837.1|  Pan troglodytes microRNA...
           28      2  gi|270133254|ref|NR_032575.1|  Macaca mulatta microRNA ...
           29      2  gi|262205445|ref|NR_030221.1|  Homo sapiens microRNA 51...
           ~~~
           97      1  gi|356517317|ref|XM_003527287.1|  PREDICTED: Glycine ma...
           98      1  gi|297814701|ref|XM_002875188.1|  Arabidopsis lyrata su...
           99      1  gi|397513516|ref|XM_003827011.1|  PREDICTED: Pan panisc...

我們才剛開始接觸物件模型的表面,但您可以看到已經有一些有用的資訊了。透過在 QueryResult 物件上調用 print,您可以看到

  • 程式名稱和版本(blastn 版本 2.2.27+)

  • 查詢 ID、描述及其序列長度(ID 為 42291,描述為「mystery_seq」,長度為 61 個核苷酸)

  • 要搜尋的目標資料庫(refseq_rna)

  • 結果命中的快速概述。對於我們的查詢序列,有 100 個潛在命中(在表格中編號為 0–99)。對於每個命中,我們還可以查看它包含多少個 HSP、其 ID 以及其描述的片段。請注意,此處 Bio.SearchIO 會截斷命中表格概述,僅顯示編號為 0–29,然後是 97–99 的命中。

現在讓我們使用與上面相同的程序來檢查我們的 BLAT 結果

>>> blat_qresult = SearchIO.read("my_blat.psl", "blat-psl")
>>> print(blat_qresult)
Program: blat (<unknown version>)
  Query: mystery_seq (61)
         <unknown description>
 Target: <unknown target>
   Hits: ----  -----  ----------------------------------------------------------
            #  # HSP  ID + description
         ----  -----  ----------------------------------------------------------
            0     17  chr19  <unknown description>

您會立即注意到有一些差異。其中一些是由 PSL 格式儲存其詳細資訊的方式引起的,您將會看到。其餘的則是由我們的 BLAST 和 BLAT 搜尋之間真正的程式和目標資料庫差異引起的

  • 程式名稱和版本。Bio.SearchIO 知道該程式是 BLAT,但在輸出檔案中沒有關於程式版本的資訊,因此預設為「<unknown version>」。

  • 查詢 ID、描述及其序列長度。請注意,此處的這些詳細資訊與我們在 BLAST 中看到的略有不同。ID 是「mystery_seq」而不是 42991,沒有已知的描述,但查詢長度仍然是 61。這實際上是檔案格式本身引入的差異。BLAST 有時會建立自己的查詢 ID,並將您的原始 ID 用作序列描述。

  • 目標資料庫未知,因為 BLAT 輸出檔案中未說明。

  • 最後,我們擁有的命中列表完全不同。在這裡,我們看到我們的查詢序列僅命中「chr19」資料庫條目,但在其中我們看到 17 個 HSP 區域。但是,考慮到我們正在使用不同的程式,每個程式都有自己的目標資料庫,這並不奇怪。

當調用 print 方法時,您看到的所有詳細資訊都可以使用 Python 的物件屬性存取符號(又稱點號表示法)單獨存取。還有其他特定於格式的屬性,您可以使用相同的方法存取。

>>> print("%s %s" % (blast_qresult.program, blast_qresult.version))
blastn 2.2.27+
>>> print("%s %s" % (blat_qresult.program, blat_qresult.version))
blat <unknown version>
>>> blast_qresult.param_evalue_threshold  # blast-xml specific
10.0

如需完整的可存取屬性列表,您可以查閱各格式的專屬文件。例如,Bio.SearchIO.BlastIOBio.SearchIO.BlatIO

在了解過如何在 QueryResult 物件上使用 print 後,讓我們深入探討。究竟什麼是 QueryResult?就 Python 物件而言,QueryResult 是列表和字典的混合體。換句話說,它是一個容器物件,兼具列表和字典的所有便利功能。

如同 Python 列表和字典,QueryResult 物件是可迭代的。每次迭代都會傳回一個 Hit 物件。

>>> for hit in blast_qresult:
...     hit
...
Hit(id='gi|262205317|ref|NR_030195.1|', query_id='42291', 1 hsps)
Hit(id='gi|301171311|ref|NR_035856.1|', query_id='42291', 1 hsps)
Hit(id='gi|270133242|ref|NR_032573.1|', query_id='42291', 1 hsps)
Hit(id='gi|301171322|ref|NR_035857.1|', query_id='42291', 2 hsps)
Hit(id='gi|301171267|ref|NR_035851.1|', query_id='42291', 1 hsps)
...

若要檢查一個 QueryResult 有多少個項目 (hits),您可以直接調用 Python 的 len 方法。

>>> len(blast_qresult)
100
>>> len(blat_qresult)
1

如同 Python 列表,您可以使用切片表示法從 QueryResult 中擷取項目 (hits)。

>>> blast_qresult[0]  # retrieves the top hit
Hit(id='gi|262205317|ref|NR_030195.1|', query_id='42291', 1 hsps)
>>> blast_qresult[-1]  # retrieves the last hit
Hit(id='gi|397513516|ref|XM_003827011.1|', query_id='42291', 1 hsps)

若要擷取多個 hits,您也可以使用切片表示法對 QueryResult 物件進行切片。在此情況下,切片將傳回一個新的 QueryResult 物件,其中僅包含切片的 hits。

>>> blast_slice = blast_qresult[:3]  # slices the first three hits
>>> print(blast_slice)
Program: blastn (2.2.27+)
  Query: 42291 (61)
         mystery_seq
 Target: refseq_rna
   Hits: ----  -----  ----------------------------------------------------------
            #  # HSP  ID + description
         ----  -----  ----------------------------------------------------------
            0      1  gi|262205317|ref|NR_030195.1|  Homo sapiens microRNA 52...
            1      1  gi|301171311|ref|NR_035856.1|  Pan troglodytes microRNA...
            2      1  gi|270133242|ref|NR_032573.1|  Macaca mulatta microRNA ...

如同 Python 字典,您也可以使用 hit 的 ID 來擷取 hits。如果您知道給定的 hit ID 存在於搜尋查詢結果中,這特別有用。

>>> blast_qresult["gi|262205317|ref|NR_030195.1|"]
Hit(id='gi|262205317|ref|NR_030195.1|', query_id='42291', 1 hsps)

您也可以使用 hits 取得完整的 Hit 物件列表,並使用 hit_keys 取得完整的 Hit ID 列表。

>>> blast_qresult.hits
[...]       # list of all hits
>>> blast_qresult.hit_keys
[...]       # list of all hit IDs

如果您只想檢查特定的 hit 是否存在於查詢結果中呢?您可以使用 in 關鍵字執行簡單的 Python 成員資格測試。

>>> "gi|262205317|ref|NR_030195.1|" in blast_qresult
True
>>> "gi|262205317|ref|NR_030194.1|" in blast_qresult
False

有時,僅知道某個 hit 是否存在是不夠的;您還想知道該 hit 的排名。在這裡,index 方法可以派上用場。

>>> blast_qresult.index("gi|301171437|ref|NR_035870.1|")
22

請記住,我們在這裡使用的是 Python 的索引樣式,它是從零開始的。這表示我們上面的 hit 排名第 23,而不是第 22。

另外,請注意您在此看到的 hit 排名是基於原始搜尋輸出檔案中存在的原生 hit 順序。不同的搜尋工具可能會根據不同的標準對這些 hit 進行排序。

如果原生 hit 順序不符合您的偏好,您可以使用 QueryResult 物件的 sort 方法。它與 Python 的 list.sort 方法非常相似,但額外提供了一個選項,可建立新的已排序 QueryResult 物件,或不建立。

以下範例說明如何使用 QueryResult.sort 根據每個 hit 的完整序列長度對 hits 進行排序。對於此特定排序,我們會將 in_place 旗標設定為 False,以便排序傳回新的 QueryResult 物件,並使我們的初始物件保持未排序狀態。我們也會將 reverse 旗標設定為 True,以便我們以降序方式排序。

>>> for hit in blast_qresult[:5]:  # id and sequence length of the first five hits
...     print("%s %i" % (hit.id, hit.seq_len))
...
gi|262205317|ref|NR_030195.1| 61
gi|301171311|ref|NR_035856.1| 60
gi|270133242|ref|NR_032573.1| 85
gi|301171322|ref|NR_035857.1| 86
gi|301171267|ref|NR_035851.1| 80

>>> sort_key = lambda hit: hit.seq_len
>>> sorted_qresult = blast_qresult.sort(key=sort_key, reverse=True, in_place=False)
>>> for hit in sorted_qresult[:5]:
...     print("%s %i" % (hit.id, hit.seq_len))
...
gi|397513516|ref|XM_003827011.1| 6002
gi|390332045|ref|XM_776818.2| 4082
gi|390332043|ref|XM_003723358.1| 4079
gi|356517317|ref|XM_003527287.1| 3251
gi|356543101|ref|XM_003539954.1| 2936

此處具有 in_place 旗標的優點是我們保留了原生順序,因此稍後可以再次使用它。您應該注意,這不是 QueryResult.sort 的預設行為,這就是為什麼我們需要將 in_place 旗標明確設定為 True 的原因。

現在,您已充分了解 QueryResult 物件,使其能夠為您工作。但在我們繼續探討 Bio.SearchIO 模型中的下一個物件之前,我們先來看看另外兩組方法,這些方法可以讓您更輕鬆地處理 QueryResult 物件:filtermap 方法。

如果您熟悉 Python 的列表推導式、產生器表達式或內建的 filtermap 函數,您就會知道它們在處理類似列表的物件時有多實用 (如果您不熟悉,請查閱它們!)。您可以使用這些內建方法來操作 QueryResult 物件,但最終會得到一般的 Python 列表,並失去進行更多有趣操作的能力。

這就是為什麼 QueryResult 物件提供其自身的 filtermap 方法的原因。與 filter 類似,有 hit_filterhsp_filter 方法。顧名思義,這些方法會根據其 Hit 物件或 HSP 物件來篩選 QueryResult 物件。同樣地,與 map 類似,QueryResult 物件也提供 hit_maphsp_map 方法。這些方法會將指定的函數分別套用到 QueryResult 物件中的所有 hit 或 HSP。

讓我們看看這些方法的實際應用,從 hit_filter 開始。此方法接受一個回呼函式,該函式會檢查給定的 Hit 物件是否通過您設定的條件。換句話說,該函式必須接受單一 Hit 物件作為其引數,並傳回 TrueFalse

以下範例說明如何使用 hit_filter 來篩選僅具有一個 HSP 的 Hit 物件。

>>> filter_func = lambda hit: len(hit.hsps) > 1  # the callback function
>>> len(blast_qresult)  # no. of hits before filtering
100
>>> filtered_qresult = blast_qresult.hit_filter(filter_func)
>>> len(filtered_qresult)  # no. of hits after filtering
37
>>> for hit in filtered_qresult[:5]:  # quick check for the hit lengths
...     print("%s %i" % (hit.id, len(hit.hsps)))
...
gi|301171322|ref|NR_035857.1| 2
gi|262205330|ref|NR_030198.1| 2
gi|301171447|ref|NR_035871.1| 2
gi|262205298|ref|NR_030190.1| 2
gi|270132717|ref|NR_032716.1| 2

hsp_filter 的運作方式與 hit_filter 相同,只是它不是檢視 Hit 物件,而是對每個 hit 中的 HSP 物件執行篩選。

至於 map 方法,它們也接受回呼函式作為其引數。但是,回呼函式不是傳回 TrueFalse,而是必須傳回修改過的 HitHSP 物件 (取決於您使用的是 hit_map 還是 hsp_map)。

讓我們看看一個範例,其中我們使用 hit_map 來重新命名 hit ID。

>>> def map_func(hit):
...     # renames "gi|301171322|ref|NR_035857.1|" to "NR_035857.1"
...     hit.id = hit.id.split("|")[3]
...     return hit
...
>>> mapped_qresult = blast_qresult.hit_map(map_func)
>>> for hit in mapped_qresult[:5]:
...     print(hit.id)
...
NR_030195.1
NR_035856.1
NR_032573.1
NR_035857.1
NR_035851.1

同樣地,hsp_map 的運作方式與 hit_map 相同,但針對的是 HSP 物件,而不是 Hit 物件。

Hit

Hit 物件代表來自單一資料庫條目的所有查詢結果。它們是 Bio.SearchIO 物件階層中的第二層容器。您已經看到它們包含在 QueryResult 物件中,但它們本身包含 HSP 物件。

讓我們看看它們是什麼樣子,從我們的 BLAST 搜尋開始。

>>> from Bio import SearchIO
>>> blast_qresult = SearchIO.read("my_blast.xml", "blast-xml")
>>> blast_hit = blast_qresult[3]  # fourth hit from the query result
>>> print(blast_hit)
Query: 42291
       mystery_seq
  Hit: gi|301171322|ref|NR_035857.1| (86)
       Pan troglodytes microRNA mir-520c (MIR520C), microRNA
 HSPs: ----  --------  ---------  ------  ---------------  ---------------------
          #   E-value  Bit score    Span      Query range              Hit range
       ----  --------  ---------  ------  ---------------  ---------------------
          0   8.9e-20     100.47      60           [1:61]                [13:73]
          1   3.3e-06      55.39      60           [0:60]                [13:73]

您可以看到我們這裡涵蓋了基本要素。

  • 查詢 ID 和描述都存在。hit 始終與查詢相關聯,因此我們也想追蹤原始查詢。可以使用 query_idquery_description 屬性從 hit 存取這些值。

  • 我們也有唯一的 hit ID、描述和完整序列長度。可以使用 iddescriptionseq_len 分別存取它們。

  • 最後,還有一個表格,其中包含此 hit 所含 HSP 的快速資訊。在每一列中,我們列出了重要的 HSP 詳細資訊:HSP 索引、其 e 值、其位元分數、其跨距 (包含間隙的比對長度)、其查詢座標和其 hit 座標。

現在,讓我們將其與 BLAT 搜尋進行比較。請記住,在 BLAT 搜尋中,我們有一個包含 17 個 HSP 的 hit。

>>> blat_qresult = SearchIO.read("my_blat.psl", "blat-psl")
>>> blat_hit = blat_qresult[0]  # the only hit
>>> print(blat_hit)
Query: mystery_seq
       <unknown description>
  Hit: chr19 (59128983)
       <unknown description>
 HSPs: ----  --------  ---------  ------  ---------------  ---------------------
          #   E-value  Bit score    Span      Query range              Hit range
       ----  --------  ---------  ------  ---------------  ---------------------
          0         ?          ?       ?           [0:61]    [54204480:54204541]
          1         ?          ?       ?           [0:61]    [54233104:54264463]
          2         ?          ?       ?           [0:61]    [54254477:54260071]
          3         ?          ?       ?           [1:61]    [54210720:54210780]
          4         ?          ?       ?           [0:60]    [54198476:54198536]
          5         ?          ?       ?           [0:61]    [54265610:54265671]
          6         ?          ?       ?           [0:61]    [54238143:54240175]
          7         ?          ?       ?           [0:60]    [54189735:54189795]
          8         ?          ?       ?           [0:61]    [54185425:54185486]
          9         ?          ?       ?           [0:60]    [54197657:54197717]
         10         ?          ?       ?           [0:61]    [54255662:54255723]
         11         ?          ?       ?           [0:61]    [54201651:54201712]
         12         ?          ?       ?           [8:60]    [54206009:54206061]
         13         ?          ?       ?          [10:61]    [54178987:54179038]
         14         ?          ?       ?           [8:61]    [54212018:54212071]
         15         ?          ?       ?           [8:51]    [54234278:54234321]
         16         ?          ?       ?           [8:61]    [54238143:54238196]

在這裡,我們獲得了與先前看到的 BLAST hit 類似的詳細程度。但有一些差異值得解釋。

  • e 值和位元分數欄的值。由於 BLAT HSP 沒有 e 值和位元分數,因此顯示預設為「?」。

  • 那麼跨距欄呢?跨距值是用來顯示完整的比對長度,其中包含所有殘基和可能存在的任何間隙。PSL 格式沒有現成可用的此資訊,而 Bio.SearchIO 不會嘗試猜測它是什麼,因此我們得到一個類似於 e 值和位元分數欄的「?」。

就 Python 物件而言,Hit 的行為與 Python 列表幾乎相同,但專門包含 HSP 物件。如果您熟悉列表,則在處理 Hit 物件時應該不會遇到任何困難。

如同 Python 列表,Hit 物件是可迭代的,並且每次迭代都會傳回一個它所包含的 HSP 物件。

>>> for hsp in blast_hit:
...     hsp
...
HSP(hit_id='gi|301171322|ref|NR_035857.1|', query_id='42291', 1 fragments)
HSP(hit_id='gi|301171322|ref|NR_035857.1|', query_id='42291', 1 fragments)

您可以對 Hit 調用 len,以查看它有多少個 HSP 物件。

>>> len(blast_hit)
2
>>> len(blat_hit)
17

您可以在 Hit 物件上使用切片符號,無論是擷取單個 HSP 或多個 HSP 物件。 就像 QueryResult 一樣,如果您對多個 HSP 進行切片,將會返回一個新的 Hit 物件,其中僅包含切片的 HSP 物件。

>>> blat_hit[0]  # retrieve single items
HSP(hit_id='chr19', query_id='mystery_seq', 1 fragments)
>>> sliced_hit = blat_hit[4:9]  # retrieve multiple items
>>> len(sliced_hit)
5
>>> print(sliced_hit)
Query: mystery_seq
       <unknown description>
  Hit: chr19 (59128983)
       <unknown description>
 HSPs: ----  --------  ---------  ------  ---------------  ---------------------
          #   E-value  Bit score    Span      Query range              Hit range
       ----  --------  ---------  ------  ---------------  ---------------------
          0         ?          ?       ?           [0:60]    [54198476:54198536]
          1         ?          ?       ?           [0:61]    [54265610:54265671]
          2         ?          ?       ?           [0:61]    [54238143:54240175]
          3         ?          ?       ?           [0:60]    [54189735:54189795]
          4         ?          ?       ?           [0:61]    [54185425:54185486]

您也可以使用與在 QueryResult 物件中看到的排序方法完全相同的參數,來排序 Hit 內的 HSP

最後,您還可以在 Hit 物件上使用 filtermap 方法。與 QueryResult 物件不同,Hit 物件只有一個版本的 filter ( Hit.filter) 和一個版本的 map ( Hit.map)。Hit.filterHit.map 都作用於 Hit 所擁有的 HSP 物件。

HSP

HSP(高分對)表示命中序列中包含與查詢序列顯著比對的區域。它包含您的查詢序列與資料庫條目之間的實際匹配。由於此匹配是由序列搜尋工具的演算法確定,因此 HSP 物件包含搜尋工具計算的大部分統計資訊。這也使得不同搜尋工具的 HSP 物件之間的差異,比起您在 QueryResultHit 物件中看到的差異更為明顯。

讓我們看一些來自我們的 BLAST 和 BLAT 搜尋的範例。我們先看看 BLAST HSP

>>> from Bio import SearchIO
>>> blast_qresult = SearchIO.read("my_blast.xml", "blast-xml")
>>> blast_hsp = blast_qresult[0][0]  # first hit, first hsp
>>> print(blast_hsp)
      Query: 42291 mystery_seq
        Hit: gi|262205317|ref|NR_030195.1| Homo sapiens microRNA 520b (MIR520...
Query range: [0:61] (1)
  Hit range: [0:61] (1)
Quick stats: evalue 4.9e-23; bitscore 111.29
  Fragments: 1 (61 columns)
     Query - CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTTTAGAGGG
             |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
       Hit - CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTTTAGAGGG

就像 QueryResultHit 一樣,在 HSP 上調用 print 會顯示其一般詳細資訊

  • 這裡有查詢和命中的 ID 和描述。我們需要這些來識別我們的 HSP

  • 我們還有查詢和命中序列的匹配範圍。我們在這裡使用的切片符號表示範圍是使用 Python 的索引風格(從零開始,半開區間)顯示的。括號內的數字表示鏈。在這種情況下,兩個序列都具有正鏈。

  • 提供了一些快速統計數據:e 值和位元分數。

  • 有關於 HSP 片段的資訊。暫時忽略這一點;稍後會解釋。

  • 最後,我們有查詢和命中序列比對本身。

這些詳細資訊可以使用點符號單獨存取,就像在 QueryResultHit 中一樣

>>> blast_hsp.query_range
(0, 61)
>>> blast_hsp.evalue
4.91307e-23

但它們並不是唯一可用的屬性。HSP 物件帶有一組預設屬性,可以輕鬆探查其各種詳細資訊。以下是一些範例

>>> blast_hsp.hit_start  # start coordinate of the hit sequence
0
>>> blast_hsp.query_span  # how many residues in the query sequence
61
>>> blast_hsp.aln_span  # how long the alignment is
61

查看 Bio.SearchIO 下的 HSP 文件,以取得這些預定義屬性的完整清單。

此外,每個序列搜尋工具通常會為其 HSP 物件計算自己的統計數據/詳細資訊。例如,XML BLAST 搜尋還會輸出間隙和相同殘基的數量。這些屬性可以這樣存取

>>> blast_hsp.gap_num  # number of gaps
0
>>> blast_hsp.ident_num  # number of identical residues
61

這些詳細資訊是特定於格式的;它們可能不存在於其他格式中。若要查看給定序列搜尋工具可用的詳細資訊,您應查看 Bio.SearchIO 中的格式文件。或者,您也可以使用 .__dict__.keys() 來快速查看可用的內容

>>> blast_hsp.__dict__.keys()
['bitscore', 'evalue', 'ident_num', 'gap_num', 'bitscore_raw', 'pos_num', '_items']

最後,您可能已經注意到我們 HSP 的 queryhit 屬性不僅僅是普通的字串

>>> blast_hsp.query
SeqRecord(seq=Seq('CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTT...GGG'), id='42291', name='aligned query sequence', description='mystery_seq', dbxrefs=[])
>>> blast_hsp.hit
SeqRecord(seq=Seq('CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTT...GGG'), id='gi|262205317|ref|NR_030195.1|', name='aligned hit sequence', description='Homo sapiens microRNA 520b (MIR520B), microRNA', dbxrefs=[])

它們是您之前在「序列註釋物件」章節中看到的 SeqRecord 物件!這表示您可以使用 SeqRecord 物件在 HSP.query 和/或 HSP.hit 上執行各種有趣的動作。

現在您應該不會感到驚訝,HSP 物件具有一個 alignment 屬性,它是一個 MultipleSeqAlignment 物件

>>> print(blast_hsp.aln)
Alignment with 2 rows and 61 columns
CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAG...GGG 42291
CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAG...GGG gi|262205317|ref|NR_030195.1|

在探討完 BLAST HSP 之後,現在讓我們看看來自 BLAT 結果的 HSP,了解不同種類的 HSP。與往常一樣,我們先在其上調用 print

>>> blat_qresult = SearchIO.read("my_blat.psl", "blat-psl")
>>> blat_hsp = blat_qresult[0][0]  # first hit, first hsp
>>> print(blat_hsp)
      Query: mystery_seq <unknown description>
        Hit: chr19 <unknown description>
Query range: [0:61] (1)
  Hit range: [54204480:54204541] (1)
Quick stats: evalue ?; bitscore ?
  Fragments: 1 (? columns)

您可能已經猜到了一些輸出。我們有查詢和命中的 ID 和描述,以及序列座標。evalue 和 bitscore 的值為 '?',因為 BLAT HSP 沒有這些屬性。但是,這裡最大的不同是您沒有看到顯示任何序列比對。如果您仔細觀察,PSL 格式本身沒有任何命中或查詢序列,因此 Bio.SearchIO 不會建立任何序列或比對物件。如果您嘗試存取 HSP.queryHSP.hitHSP.aln,會發生什麼事?您會取得這些屬性的預設值,即 None

>>> blat_hsp.hit is None
True
>>> blat_hsp.query is None
True
>>> blat_hsp.aln is None
True

不過,這不會影響其他屬性。例如,您仍然可以存取查詢或命中比對的長度。儘管未顯示任何屬性,PSL 格式仍然有此資訊,因此 Bio.SearchIO 可以擷取它們

>>> blat_hsp.query_span  # length of query match
61
>>> blat_hsp.hit_span  # length of hit match
61

其他特定於格式的屬性仍然存在

>>> blat_hsp.score  # PSL score
61
>>> blat_hsp.mismatch_num  # the mismatch column
0

到目前為止還不錯嗎?當您查看 BLAT 結果中存在的另一個「變體」的 HSP 時,事情會變得更有趣。您可能還記得,在 BLAT 搜尋中,有時我們的結果會分成「區塊」。這些區塊本質上是一些比對片段,它們之間可能有一些間隔序列。

讓我們看看一個包含多個區塊的 BLAT HSP,看看 Bio.SearchIO 如何處理這種情況

>>> blat_hsp2 = blat_qresult[0][1]  # first hit, second hsp
>>> print(blat_hsp2)
      Query: mystery_seq <unknown description>
        Hit: chr19 <unknown description>
Query range: [0:61] (1)
  Hit range: [54233104:54264463] (1)
Quick stats: evalue ?; bitscore ?
  Fragments: ---  --------------  ----------------------  ----------------------
               #            Span             Query range               Hit range
             ---  --------------  ----------------------  ----------------------
               0               ?                  [0:18]     [54233104:54233122]
               1               ?                 [18:61]     [54264420:54264463]

這裡發生什麼事?我們仍然涵蓋了一些基本詳細資訊:ID 和描述、座標,以及快速統計數據與您之前看到的類似。但是片段詳細資訊完全不同。我們現在有一個包含兩個數據列的表格,而不是顯示「片段:1」。

這就是 Bio.SearchIO 處理具有多個片段的 HSP 的方式。如前所述,HSP 比對可能會被間隔序列分成片段。間隔序列不是查詢命中匹配的一部分,因此不應將它們視為查詢或命中序列的一部分。但是,它們確實會影響我們處理序列座標的方式,因此我們不能忽略它們。

看一下上面 HSP 的命中座標。在 Hit range: 欄位中,我們看到座標是 [54233104:54264463]。但是,查看表格列,我們看到此座標跨越的整個區域並未與我們的查詢匹配。具體而言,間隔區域從 5423312254264420

那麼,您可能會問,為什麼查詢座標看起來是連續的?這是完全可以的。在這種情況下,這表示查詢匹配是連續的(沒有間隔區域),而命中匹配不是。

順帶一提,這些屬性都可以直接從 HSP 存取

>>> blat_hsp2.hit_range  # hit start and end coordinates of the entire HSP
(54233104, 54264463)
>>> blat_hsp2.hit_range_all  # hit start and end coordinates of each fragment
[(54233104, 54233122), (54264420, 54264463)]
>>> blat_hsp2.hit_span  # hit span of the entire HSP
31359
>>> blat_hsp2.hit_span_all  # hit span of each fragment
[18, 43]
>>> blat_hsp2.hit_inter_ranges  # start and end coordinates of intervening regions in the hit sequence
[(54233122, 54264420)]
>>> blat_hsp2.hit_inter_spans  # span of intervening regions in the hit sequence
[31298]

這些屬性大多不是可以從我們的 PSL 檔案直接取得,但是當您剖析 PSL 檔案時,Bio.SearchIO 會即時為您計算它們。它只需要每個片段的開始和結束座標。

那麼 queryhitaln 屬性呢?如果 HSP 有多個片段,您將無法使用這些屬性,因為它們只會擷取單個 SeqRecordMultipleSeqAlignment 物件。但是,您可以使用它們的 *_all 對應物:query_allhit_allaln_all。這些屬性會傳回一個清單,其中包含來自每個 HSP 片段的 SeqRecordMultipleSeqAlignment 物件。還有其他屬性的行為類似,也就是說,它們僅適用於具有一個片段的 HSP。請查看 Bio.SearchIO 下的 HSP 文件,以取得完整清單。

最後,若要檢查您是否有多個片段,您可以使用 is_fragmented 屬性,如下所示

>>> blat_hsp2.is_fragmented  # BLAT HSP with 2 fragments
True
>>> blat_hsp.is_fragmented  # BLAT HSP from earlier, with one fragment
False

在我們繼續之前,您還應該知道,我們可以在 HSP 物件上使用切片 (slice) 符號,就像 QueryResultHit 物件一樣。當您使用這種符號時,您將會得到一個 HSPFragment 物件作為回傳值,它是物件模型的最後一個組件。

HSPFragment

HSPFragment 代表查詢序列和命中序列之間單一、連續的匹配。您可以將其視為物件模型和搜尋結果的核心,因為這些片段的存在與否決定了您的搜尋是否有結果。

在大多數情況下,您不必直接處理 HSPFragment 物件,因為沒有那麼多序列搜尋工具會將它們的 HSP 分割成片段。當您必須處理它們時,您應該記住 HSPFragment 物件的設計盡可能地精簡。在大多數情況下,它們只包含直接與序列相關的屬性:股 (strands)、讀碼框架 (reading frames)、分子類型、座標、序列本身,以及它們的 ID 和描述。

當您在 HSPFragment 上調用 print 時,這些屬性會立即顯示出來。以下是一個範例,取自我們的 BLAST 搜尋結果。

>>> from Bio import SearchIO
>>> blast_qresult = SearchIO.read("my_blast.xml", "blast-xml")
>>> blast_frag = blast_qresult[0][0][0]  # first hit, first hsp, first fragment
>>> print(blast_frag)
      Query: 42291 mystery_seq
        Hit: gi|262205317|ref|NR_030195.1| Homo sapiens microRNA 520b (MIR520...
Query range: [0:61] (1)
  Hit range: [0:61] (1)
  Fragments: 1 (61 columns)
     Query - CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTTTAGAGGG
             |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
       Hit - CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTTTAGAGGG

在這個層級,BLAT 片段看起來與 BLAST 片段非常相似,只是缺少查詢序列和命中序列。

>>> blat_qresult = SearchIO.read("my_blat.psl", "blat-psl")
>>> blat_frag = blat_qresult[0][0][0]  # first hit, first hsp, first fragment
>>> print(blat_frag)
      Query: mystery_seq <unknown description>
        Hit: chr19 <unknown description>
Query range: [0:61] (1)
  Hit range: [54204480:54204541] (1)
  Fragments: 1 (? columns)

在所有情況下,都可以使用我們最喜歡的點號符號來存取這些屬性。以下是一些範例。

>>> blast_frag.query_start  # query start coordinate
0
>>> blast_frag.hit_strand  # hit sequence strand
1
>>> blast_frag.hit  # hit sequence, as a SeqRecord object
SeqRecord(seq=Seq('CCCTCTACAGGGAAGCGCTTTCTGTTGTCTGAAAGAAAAGAAAGTGCTTCCTTT...GGG'), id='gi|262205317|ref|NR_030195.1|', name='aligned hit sequence', description='Homo sapiens microRNA 520b (MIR520B), microRNA', dbxrefs=[])

關於標準和慣例的說明

在我們繼續討論主要函數之前,您應該了解 Bio.SearchIO 使用的標準。如果您使用過多種序列搜尋工具,您可能不得不處理每個程式處理序列座標等方式的差異。這可能不是愉快的體驗,因為這些搜尋工具通常有自己的標準。例如,一個工具可能使用基於 1 的座標,而另一個工具使用基於 0 的座標。或者,一個程式可能會在股為負時反轉起始和結束座標,而其他程式則不會。簡而言之,這些通常會產生不必要的混亂,必須加以處理。

我們自己也意識到這個問題,並打算在 Bio.SearchIO 中解決它。畢竟,Bio.SearchIO 的目標之一是建立一個通用、易於使用的介面,以處理各種搜尋輸出檔案。這意味著建立超出您剛才看到的物件模型的標準。

現在,您可能會抱怨:「又一個標準!」 嗯,最終我們必須選擇一種或另一種慣例,所以這是必要的。此外,我們在這裡不是創造全新的東西;只是採用我們認為最適合 Python 程式設計師的標準(畢竟它是 Biopython)。

當您使用 Bio.SearchIO 時,可以預期有三個隱含的標準。

  • 第一個與序列座標有關。在 Bio.SearchIO 中,所有序列座標都遵循 Python 的座標樣式:基於 0 且半開。例如,如果 BLAST XML 輸出檔案中 HSP 的起始和結束座標分別為 10 和 28,則在 Bio.SearchIO 中它們將變為 9 和 28。起始座標變為 9 是因為 Python 索引從零開始,而結束座標保持 28 不變,因為 Python 切片會省略區間中的最後一個項目。

  • 第二個是關於序列座標順序。在 Bio.SearchIO 中,起始座標始終小於或等於結束座標。並非所有序列搜尋工具都如此,因為當序列股為負時,它們中的某些工具的起始座標較大。

  • 最後一個是關於股和讀碼框架的值。對於股,只有四個有效的選擇:1(正股)、-1(負股)、0(蛋白質序列)和 None(無股)。對於讀碼框架,有效的選擇是從 -33 的整數和 None

請注意,這些標準僅存在於 Bio.SearchIO 物件中。如果您將 Bio.SearchIO 物件寫入輸出格式,Bio.SearchIO 將使用該格式的標準進行輸出。它不會將其標準強制應用到您的輸出檔案。

讀取搜尋輸出檔案

您可以使用兩個函數將搜尋輸出檔案讀取到 Bio.SearchIO 物件中:readparse。它們與其他子模組(如 Bio.SeqIOBio.AlignIO)中的 readparse 函數基本相似。在這兩種情況下,您都需要提供搜尋輸出檔案名稱和檔案格式名稱,兩者都使用 Python 字串表示。您可以查看文件,以取得 Bio.SearchIO 識別的格式名稱清單。

Bio.SearchIO.read 用於讀取只有一個查詢的搜尋輸出檔案,並返回一個 QueryResult 物件。您已經在我們之前的範例中看到使用 read。您沒有看到的是,read 也可能接受其他關鍵字參數,具體取決於檔案格式。

以下是一些範例。在第一個範例中,我們像之前一樣使用 read 來讀取 BLAST 表格輸出檔案。在第二個範例中,我們使用關鍵字參數進行修改,以便它解析包含註解的 BLAST 表格變體。

>>> from Bio import SearchIO
>>> qresult = SearchIO.read("tab_2226_tblastn_003.txt", "blast-tab")
>>> qresult
QueryResult(id='gi|16080617|ref|NP_391444.1|', 3 hits)
>>> qresult2 = SearchIO.read("tab_2226_tblastn_007.txt", "blast-tab", comments=True)
>>> qresult2
QueryResult(id='gi|16080617|ref|NP_391444.1|', 3 hits)

這些關鍵字參數在不同的檔案格式之間有所不同。請查看格式文件,以了解它是否有修改其解析器行為的關鍵字參數。

至於 Bio.SearchIO.parse,它用於讀取具有任意數量的查詢的搜尋輸出檔案。該函數返回一個生成器物件,該物件在每次迭代時產生一個 QueryResult 物件。與 Bio.SearchIO.read 一樣,它也接受格式特定的關鍵字參數。

>>> from Bio import SearchIO
>>> qresults = SearchIO.parse("tab_2226_tblastn_001.txt", "blast-tab")
>>> for qresult in qresults:
...     print(qresult.id)
...
gi|16080617|ref|NP_391444.1|
gi|11464971:4-101
>>> qresults2 = SearchIO.parse("tab_2226_tblastn_005.txt", "blast-tab", comments=True)
>>> for qresult in qresults2:
...     print(qresult.id)
...
random_s00
gi|16080617|ref|NP_391444.1|
gi|11464971:4-101

使用索引處理大型搜尋輸出檔案

有時,您會收到一個包含數百或數千個需要解析的查詢的搜尋輸出檔案。您當然可以使用 Bio.SearchIO.parse 來處理此檔案,但如果您只需要存取少數幾個查詢,那將會非常沒有效率。這是因為 parse 會在提取您感興趣的查詢之前解析它看到的所有查詢。

在這種情況下,理想的選擇是使用 Bio.SearchIO.indexBio.SearchIO.index_db 來索引檔案。如果這些名稱聽起來很熟悉,那是因為您之前在 將序列檔案視為字典 – 索引檔案 一節中看過它們。這些函數的行為也與它們的 Bio.SeqIO 對應項類似,並新增了格式特定的關鍵字參數。

以下是一些範例。您可以只使用檔案名稱和格式名稱來使用 index

>>> from Bio import SearchIO
>>> idx = SearchIO.index("tab_2226_tblastn_001.txt", "blast-tab")
>>> sorted(idx.keys())
['gi|11464971:4-101', 'gi|16080617|ref|NP_391444.1|']
>>> idx["gi|16080617|ref|NP_391444.1|"]
QueryResult(id='gi|16080617|ref|NP_391444.1|', 3 hits)
>>> idx.close()

或者也可以使用格式特定的關鍵字參數。

>>> idx = SearchIO.index("tab_2226_tblastn_005.txt", "blast-tab", comments=True)
>>> sorted(idx.keys())
['gi|11464971:4-101', 'gi|16080617|ref|NP_391444.1|', 'random_s00']
>>> idx["gi|16080617|ref|NP_391444.1|"]
QueryResult(id='gi|16080617|ref|NP_391444.1|', 3 hits)
>>> idx.close()

或者使用 key_function 參數,就像在 Bio.SeqIO 中一樣。

>>> key_function = lambda id: id.upper()  # capitalizes the keys
>>> idx = SearchIO.index("tab_2226_tblastn_001.txt", "blast-tab", key_function=key_function)
>>> sorted(idx.keys())
['GI|11464971:4-101', 'GI|16080617|REF|NP_391444.1|']
>>> idx["GI|16080617|REF|NP_391444.1|"]
QueryResult(id='gi|16080617|ref|NP_391444.1|', 3 hits)
>>> idx.close()

Bio.SearchIO.index_db 的工作方式與 index 類似,只不過它將查詢偏移量寫入 SQLite 資料庫檔案。

寫入和轉換搜尋輸出檔案

有時,能夠操作輸出檔案中的搜尋結果並將其再次寫入新檔案是很有用的。Bio.SearchIO 提供了一個 write 函數,讓您可以做到這一點。它將返回 QueryResult 物件的可迭代物件、要寫入的輸出檔案名稱、要寫入的格式名稱,以及一些格式特定的關鍵字參數(可選)作為參數。它返回一個包含四個項目的元組,表示已寫入的 QueryResultHitHSPHSPFragment 物件的數量。

>>> from Bio import SearchIO
>>> qresults = SearchIO.parse("mirna.xml", "blast-xml")  # read XML file
>>> SearchIO.write(qresults, "results.tab", "blast-tab")  # write to tabular file
(3, 239, 277, 277)

您應該注意,不同的檔案格式需要 QueryResultHitHSPHSPFragment 物件的不同屬性。如果這些屬性不存在,則寫入將不起作用。換句話說,您無法總是寫入您想要的輸出格式。例如,如果您讀取 BLAST XML 檔案,您將無法將結果寫入 PSL 檔案,因為 PSL 檔案需要 BLAST 未計算的屬性(例如,重複匹配的數量)。如果您真的想寫入 PSL,您可以隨時手動設定這些屬性。

readparseindexindex_db 一樣,write 也接受格式特定的關鍵字參數。請查看文件,以取得 Bio.SearchIO 可以寫入的格式及其參數的完整清單。

最後,Bio.SearchIO 也提供了一個 convert 函數,它只是 Bio.SearchIO.parseBio.SearchIO.write 的快捷方式。使用 convert 函數,我們上面的例子會變成這樣:

>>> from Bio import SearchIO
>>> SearchIO.convert("mirna.xml", "blast-xml", "results.tab", "blast-tab")
(3, 239, 277, 277)

由於 convert 使用 write,它僅限於那些具有所有必要屬性的格式轉換。在這裡,BLAST XML 檔案提供了 BLAST 表格檔案所需的所有預設值,因此它可以正常運作。然而,其他格式轉換不太可能運作,因為您需要先手動指定必要的屬性。