序列比对——needleman wunsch算法及结果呈现

基本原理

两条序列每个元素比对无非三种情况:1序列对空,2序列对空,两序列配对。该算法从序列最左端开始,依次按照这三种情况打分,每下一个元素的配对情况都是按照上一个元素的最优解往下的。直到将打分矩阵铺满,打分矩阵右下角的得分情况即说明两序列配对总得分的最优解。再通过路线的回溯得到比对的几种情况。

M[i][j] = max(M[i-1][j]+gap,M[i][j-1]+gap,M[i-1][j-1]+point)

gap:未配对罚分;point:配对得分(包括错配和正确配对)

示例:

将catgt和acgctg按照上面的规则打分

gap=-1 wrong point=-1 point=2

gapcatgt
gap0-1-2-3-4-5
a-1-110-1-1
c-2100-1-2
g-300-121
c-4-1-1-111
t-5-2-2103
g-6-3-3032

将每个得分的来源用箭头表示,发现有三条线路是首尾连通的且分值最大

↘表示配对成功,↓的箭头的位置所在左边的碱基对空,→与前者相对

得分矩阵

BLOSUM62矩阵是经过大量实验验证出来正确性的得分矩阵,相比于PAM矩阵的预测得分来说更符合真实情况。

"BLOSUM62": {
    "AA": "4", "AC": "0", "AD": "-2", "AE": "-1", "AF": "-2", "AG": "0", "AH": "-2", "AI": "-1", "AK": "-1", "AL": "-1", "AM": "-1", "AN": "-2", "AP": "-1", "AQ": "-1", "AR": "-1", "AS": "1", "AT": "0", "AV": "0", "AW": "-3", "AY": "-2", "CA": "0", "CC": "9", "CD": "-3", "CE": "-4", "CF": "-2", "CG": "-3", "CH": "-3", "CI": "-1", "CK": "-3", "CL": "-1", "CM": "-1", "CN": "-3", "CP": "-3", "CQ": "-3", "CR": "-3", "CS": "-1", "CT": "-1", "CV": "-1", "CW": "-2", "CY": "-2", "DA": "-2", "DC": "-3", "DD": "6", "DE": "2", "DF": "-3", "DG": "-1", "DH": "-1", "DI": "-3", "DK": "-1", "DL": "-4", "DM": "-3", "DN": "1", "DP": "-1", "DQ": "0", "DR": "-2", "DS": "0", "DT": "-1", "DV": "-3", "DW": "-4", "DY": "-3", "EA": "-1", "EC": "-4", "ED": "2", "EE": "5", "EF": "-3", "EG": "-2", "EH": "0", "EI": "-3", "EK": "1", "EL": "-3", "EM": "-2", "EN": "0", "EP": "-1", "EQ": "2", "ER": "0", "ES": "0", "ET": "-1", "EV": "-2", "EW": "-3", "EY": "-2", "FA": "-2", "FC": "-2", "FD": "-3", "FE": "-3", "FF": "6", "FG": "-3", "FH": "-1", "FI": "0", "FK": "-3", "FL": "0", "FM": "0", "FN": "-3", "FP": "-4", "FQ": "-3", "FR": "-3", "FS": "-2", "FT": "-2", "FV": "-1", "FW": "1", "FY": "3", "GA": "0", "GC": "-3", "GD": "-1", "GE": "-2", "GF": "-3", "GG": "6", "GH": "-2", "GI": "-4", "GK": "-2", "GL": "-4", "GM": "-3", "GN": "0", "GP": "-2", "GQ": "-2", "GR": "-2", "GS": "0", "GT": "-2", "GV": "-3", "GW": "-2", "GY": "-3", "HA": "-2", "HC": "-3", "HD": "-1", "HE": "0", "HF": "-1", "HG": "-2", "HH": "8", "HI": "-3", "HK": "-1", "HL": "-3", "HM": "-2", "HN": "1", "HP": "-2", "HQ": "0", "HR": "0", "HS": "-1", "HT": "-2", "HV": "-3", "HW": "-2", "HY": "2", "IA": "-1", "IC": "-1", "ID": "-3", "IE": "-3", "IF": "0", "IG": "-4", "IH": "-3", "II": "4", "IK": "-3", "IL": "2", "IM": "1", "IN": "-3", "IP": "-3", "IQ": "-3", "IR": "-3", "IS": "-2", "IT": "-1", "IV": "3", "IW": "-3", "IY": "-1", "KA": "-1", "KC": "-3", "KD": "-1", "KE": "1", "KF": "-3", "KG": "-2", "KH": "-1", "KI": "-3", "KK": "5", "KL": "-2", "KM": "-1", "KN": "0", "KP": "-1", "KQ": "1", "KR": "2", "KS": "0", "KT": "-1", "KV": "-2", "KW": "-3", "KY": "-2", "LA": "-1", "LC": "-1", "LD": "-4", "LE": "-3", "LF": "0", "LG": "-4", "LH": "-3", "LI": "2", "LK": "-2", "LL": "4", "LM": "2", "LN": "-3", "LP": "-3", "LQ": "-2", "LR": "-2", "LS": "-2", "LT": "-1", "LV": "1", "LW": "-2", "LY": "-1", "MA": "-1", "MC": "-1", "MD": "-3", "ME": "-2", "MF": "0", "MG": "-3", "MH": "-2", "MI": "1", "MK": "-1", "ML": "2", "MM": "5", "MN": "-2", "MP": "-2", "MQ": "0", "MR": "-1", "MS": "-1", "MT": "-1", "MV": "1", "MW": "-1", "MY": "-1", "NA": "-2", "NC": "-3", "ND": "1", "NE": "0", "NF": "-3", "NG": "0", "NH": "1", "NI": "-3", "NK": "0", "NL": "-3", "NM": "-2", "NN": "6", "NP": "-2", "NQ": "0", "NR": "0", "NS": "1", "NT": "0", "NV": "-3", "NW": "-4", "NY": "-2", "PA": "-1", "PC": "-3", "PD": "-1", "PE": "-1", "PF": "-4", "PG": "-2", "PH": "-2", "PI": "-3", "PK": "-1", "PL": "-3", "PM": "-2", "PN": "-2", "PP": "7", "PQ": "-1", "PR": "-2", "PS": "-1", "PT": "-1", "PV": "-2", "PW": "-4", "PY": "-3", "QA": "-1", "QC": "-3", "QD": "0", "QE": "2", "QF": "-3", "QG": "-2", "QH": "0", "QI": "-3", "QK": "1", "QL": "-2", "QM": "0", "QN": "0", "QP": "-1", "QQ": "5", "QR": "1", "QS": "0", "QT": "-1", "QV": "-2", "QW": "-2", "QY": "-1", "RA": "-1", "RC": "-3", "RD": "-2", "RE": "0", "RF": "-3", "RG": "-2", "RH": "0", "RI": "-3", "RK": "2", "RL": "-2", "RM": "-1", "RN": "0", "RP": "-2", "RQ": "1", "RR": "5", "RS": "-1", "RT": "-1", "RV": "-3", "RW": "-3", "RY": "-2", "SA": "1", "SC": "-1", "SD": "0", "SE": "0", "SF": "-2", "SG": "0", "SH": "-1", "SI": "-2", "SK": "0", "SL": "-2", "SM": "-1", "SN": "1", "SP": "-1", "SQ": "0", "SR": "-1", "SS": "4", "ST": "1", "SV": "-2", "SW": "-3", "SY": "-2", "TA": "0", "TC": "-1", "TD": "-1", "TE": "-1", "TF": "-2", "TG": "-2", "TH": "-2", "TI": "-1", "TK": "-1", "TL": "-1", "TM": "-1", "TN": "0", "TP": "-1", "TQ": "-1", "TR": "-1", "TS": "1", "TT": "5", "TV": "0", "TW": "-2", "TY": "-2", "VA": "0", "VC": "-1", "VD": "-3", "VE": "-2", "VF": "-1", "VG": "-3", "VH": "-3", "VI": "3", "VK": "-2", "VL": "1", "VM": "1", "VN": "-3", "VP": "-2", "VQ": "-2", "VR": "-3", "VS": "-2", "VT": "0", "VV": "4", "VW": "-3", "VY": "-1", "WA": "-3", "WC": "-2", "WD": "-4", "WE": "-3", "WF": "1", "WG": "-2", "WH": "-2", "WI": "-3", "WK": "-3", "WL": "-2", "WM": "-1", "WN": "-4", "WP": "-4", "WQ": "-2", "WR": "-3", "WS": "-3", "WT": "-2", "WV": "-3", "WW": "11", "WY": "2", "YA": "-2", "YC": "-2", "YD": "-3", "YE": "-2", "YF": "3", "YG": "-3", "YH": "2", "YI": "-1", "YK": "-2", "YL": "-1", "YM": "-1", "YN": "-2", "YP": "-3", "YQ": "-1", "YR": "-2", "YS": "-2", "YT": "-2", "YV": "-1", "YW": "2", "YY": "7"
  },
"ATCG": {
  "AA": 2, "AT": -1, "AC": -1, "AG": -1,
  "TA": -1, "TT": 2, "TC": -1, "TG": -1,
  "CA": -1, "CT": -1, "CC": 2, "CG": -1,
  "GA": -1, "GT": -1, "GC": -1, "GG": 2
}

代码思路

路径生成

在得分矩阵每一个分值生成时,判断其分值的来源,考虑存在多个来源的情况,生成一个表示去向的矩阵,由xyz分别表示下,右,右下,无路可走就用空集表示,由于最左上角一定可以往右或往下,方便起见,在初始的方位矩阵左上角手动加上y和x。将方位储存在road里

gap = -2
seq1 = '-'+seq1
seq2 = '-'+seq2    #由于在打分时要对序列“前一个”不存在的碱基比对,因此加上“-”
martix = [[(i*gap)+(j*gap) for i in range(len(seq1))]for j in range(len(seq2))]#生成打分矩阵的第一行和第一列,其余部分由于后面会被修正,所以可以不用管
road = [[[] for i in seq1] for j in seq2]
len1 = len(seq1)
len2 = len(seq2)

for i in range(1,len2):
    for j in range(1,len1):
        score_from_left = martix[i][j - 1] + gap
        score_from_diag = martix[i - 1][j - 1] + int(a[f'{seq1[j]}{seq2[i]}'])
        score_from_up = martix[i - 1][j] + gap
        maxim=max(martix[i][j-1]+gap,martix[i-1][j-1]+int(a[f'{seq1[j]}{seq2[i]}']),martix[i-1][j]+gap)

        #判断分值来源
        if maxim == score_from_left:
            road[i][j-1].append('y')
        if maxim == score_from_diag:
            road[i-1][j-1].append('z')
        if maxim == score_from_up:
            road[i-1][j].append('x')

        #生成打分矩阵
        martix[i][j]=max(martix[i][j-1]+gap,martix[i-1][j-1]+int(a[f'{seq1[j]}{seq2[i]}']),martix[i-1][j]+gap)

#手动给左上角加上右和下方向
road[0][0].append('x')
road[0][0].append('y')

深度优先算法寻找所有比对可能性

关于深度优先算法,简而言之就是走迷宫从入口开始一直贴着右边墙壁或者左边墙壁走,在上面有箭头的图自己走走试试看

def find_path(martix):
    paths = []
    def depth_first(r, l, current_path):
        
        #如果到达右下角,把路径记录下来,return即出栈
        if r == len(martix) - 1 and l == len(martix[1]) - 1:
            paths.append(current_path)
            return

        #识别到相关方位,即朝着此方位前进
        for i in martix[r][l]:
            if i == 'y' and l < len(martix[1]) - 1:
                depth_first(r, l + 1, current_path + ['y'])#这一句包括了堆栈操作
            elif i == 'x' and r < len(martix) - 1:
                depth_first(r + 1, l, current_path + ['x'])
            elif i == 'z' and l < len(martix[1]) - 1 and r < len(martix) - 1:
                depth_first(r + 1, l + 1, current_path + ['z'])

    depth_first(0, 0, [])
    return paths

输出的结果是若干条线路,从左上角出发,按照xyz到右下角的线路

按照线路输出比对结果

这段代码比较简单,不多赘述

a = len(find_path(road))
r1 = {i:'' for i in range(a) }
r2 = {i:'' for i in range(a) }
for i in range(len(find_path(road))):
    x = 0
    y = 0
    for j in find_path(road)[i]:
        if j == 'x':
            r1[i] = r1[i]+'-'
            r2[i] = r2[i]+seq2[y+1]
            y+=1
        elif j == 'y':
            r2[i] = r2[i]+'-'
            r1[i] = r1[i]+seq1[x+1]
            x+=1
        elif j == 'z':
            r1[i] = r1[i]+seq1[x+1]
            r2[i] = r2[i]+seq2[y+1]
            x += 1
            y += 1

总代码如下

import json
def blastp(seq1,seq2):
    def find_path(martix):
        paths = []
        def depth_first(r, l, current_path):
            if r == len(martix) - 1 and l == len(martix[1]) - 1:
                paths.append(current_path)
                return
            for i in martix[r][l]:
                if i == 'y' and l < len(martix[1]) - 1:
                    depth_first(r, l + 1, current_path + ['y'])
                elif i == 'x' and r < len(martix) - 1:
                    depth_first(r + 1, l, current_path + ['x'])
                elif i == 'z' and l < len(martix[1]) - 1 and r < len(martix) - 1:
                    depth_first(r + 1, l + 1, current_path + ['z'])
        depth_first(0, 0, [])
        return paths

    with open('/juzhen.json', 'r') as BLOSUM:
        a = json.load(BLOSUM)['BLOSUM62']         #根据需要更改

    gap = -2           #根据需要更改
    seq1 = '-'+seq1
    seq2 = '-'+seq2
    martix = [[(i*gap)+(j*gap) for i in range(len(seq1))]for j in range(len(seq2))]
    len1 = len(seq1)
    len2 = len(seq2)
    road = [[[] for i in seq1] for j in seq2]
    road[0][0].append('x')
    for i in range(1,len2):
        for j in range(1,len1):
            score_from_left = martix[i][j - 1] + gap
            score_from_diag = martix[i - 1][j - 1] + int(a[f'{seq1[j]}{seq2[i]}'])
            score_from_up = martix[i - 1][j] + gap
            maxim=max(martix[i][j-1]+gap,martix[i-1][j-1]+int(a[f'{seq1[j]}{seq2[i]}']),martix[i-1][j]+gap)
            if maxim == score_from_left:
                road[i][j-1].append('y')
            if maxim == score_from_diag:
                road[i-1][j-1].append('z')
            if maxim == score_from_up:
                road[i-1][j].append('x')
            martix[i][j]=max(martix[i][j-1]+gap,martix[i-1][j-1]+int(a[f'{seq1[j]}{seq2[i]}']),martix[i-1][j]+gap)
    road[0][0].append('y')

    a = len(find_path(road))
    r1 = {i:'' for i in range(a) }
    r2 = {i:'' for i in range(a) }
    for i in range(len(find_path(road))):
        x = 0
        y = 0
        for j in find_path(road)[i]:
            if j == 'x':
                r1[i] = r1[i]+'-'
                r2[i] = r2[i]+seq2[y+1]
                y+=1
            elif j == 'y':
                r2[i] = r2[i]+'-'
                r1[i] = r1[i]+seq1[x+1]
                x+=1
            elif j == 'z':
                r1[i] = r1[i]+seq1[x+1]
                r2[i] = r2[i]+seq2[y+1]
                x += 1
                y += 1
    print(r1)
    print(r2)

seq1 = ''    #在这里输入蛋白质序列
seq2 = ''

print(blastp(seq1,seq2))

此算法精度极高,但是一般不用于较长的序列,因为时间复杂度较高,且会将所有结果都显示出来。当空位罚分<-2时运行速度较快

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值