BWT 算法和序列比對的基本實現

昨天晚上和今天抽空實現了Burrows Wheleer Tansform,並且嘗試利用BWT,將短序列比對到長序列中。BWT的核心我覺得是要理解兩個原則:

1. F序列的每個元素是下標對應的L元素的後一位。

2. 排序後,F中第一個A和L中第一個A是同一個A。(排序不改變相對位置),公共前綴不改變排序位置。 

mapping 過程實現的非常基礎,只能全序列不對,不能有gap。

#!/usr/bin/env python3
'''
burrows wheleer transform
核心是 排序後的F,L數組, L數組的某個元素是F數組對應的元素的前一位。
其次是,F數組的某個元素A在所有A中的相對位置不變。也就是F中的第一個A對應着L中的第一個A.
'''

import os
import sys
from collections import Counter


def BWT(s, end):
    '''
    輸出1:L string
    輸出2:L string 每個字符的原來的位置
    '''
    # 記錄原序列中字符出現位置
    C = {i: [] for i in set(s)}  # 初始化L列的元素相對位置
    for i in range(len(s)):  # 分別給每個元素標記出現的位置
        C[s[i]].append(i)

    s = s + end  # 添加最後的符號
    M = []  # 初始化輸出
    count = len(s)  # 計算字符串長度

    # 設置偏移矩陣
    while True:
        M.append(s)
        s = s[len(s)-1]+s[0:len(s)-1]
        count -= 1
        if count <= 0:
            break
    N = [[v, k] for k, v in enumerate(M[::-1])] # 標記位置信息
    N_sort = sorted(N, key=lambda x: x[0])  #字典排序
    C = {i: [] for i in set(s)}  # 初始化L列的元素相對位置
    L = []
    for k, v in N_sort: # 以A爲例,Lstring中 從頭開始每一個A在原來字符串的位置 
        L.append(k[-1])
        C[k[-1]].append(v)

    return(["".join(L), C])


def reverseBWT(s, end):
    '''
    '''

    B = Counter(s)  # 壓縮的F列
    C = {i: [] for i in set(B)}  # 初始化L列的元素相對位置
    for i in range(len(s)):  # 分別給每個元素標記出現的位置
        C[s[i]].append(i)

    out = ""  # 初始化輸出
    arrow = 1
    while True:
        Lbase = s[arrow-1]  # 取出對應的字符
        out = out + Lbase  # 添加到輸出

        if Lbase == end:  # 如果遇到終止符,則退出循環,並輸出
            break
        # 從數組C中查找對應字符出現的位置,並且枚舉,這樣可以得到字符所在位置對應的相同字符的偏移量。
        for i, j in enumerate(C[Lbase]):
            if j == arrow-1:
                pianyi = i+1  # 這裏應該是對應的L中的序號
                break

        arrow = CheckF(B, Lbase, pianyi)
    return(out[::-1])


def simple_mapping(ref, seed):

    s, rank = BWT(ref, "#")

    B = Counter(s)  # 壓縮的F列
    C = {i: [] for i in set(s)}  # 初始化L列的元素相對位置
    for i in range(len(s)):  # 分別給每個元素標記出現的位置
        C[s[i]].append(i)

    seed = seed[::-1]
    print(ref)

    for mark in range(len(C[seed[0]])):
        arrow = C[seed[0]][mark]  # mark:0,1,2 編號,0開始
        # arrow: L 中的 12,34,65... 實際位置

        out = ""
        count = 0
        while True:
            Lbase = s[arrow]
            if Lbase != seed[count] or Lbase == '#':
                break
            out = out + Lbase
            for i, v in enumerate(C[Lbase]):
                if v == arrow:
                    pianyi = i + 1
                    break
            arrow = CheckF_0base(B, Lbase, pianyi)
            count += 1
            if count > len(seed)-1:
                left = rank[seed[0]][mark]
                out = "*"*(left-len(out)+1) + out[::-1] + "*"*(len(s)-left-2)
                print(out)
                break


def CheckF(mtx, base, order):
    out_order = 0
    for k in sorted(mtx.keys()):  # 從F數組中根據字符,以及偏移量,計算出下一個座標 P = 字符前所有的字符的數目+偏移量
        if base != k:
            out_order += mtx[k]
        else:
            out_order += order
            break
    return(out_order)


def CheckF_0base(mtx, base, order):
    out_order = 0
    for k in sorted(mtx.keys()):  # 從F數組中根據字符,以及偏移量,計算出下一個座標 P = 字符前所有的字符的數目+偏移量
        if base != k:
            out_order += mtx[k]
        else:
            out_order += order
            break
    return(out_order-1)


if __name__ == '__main__':

    x = "actgagctttagcgtagctttaggagagcttcctagctacgtatcgagcgggcatctatc"
    seed = "ga"
    print("origin string is :" + x)
    print("L string      is :" + BWT(x, '#')[0])
    print("seed string   is :" + seed)
    print("mapped seq    is :")
    simple_mapping(x, seed)

 

 

結果:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章