【序列比对】Needleman-Wunsch(全局)和Smith-Waterman(局部)算法py实现(多条回溯路径,三叉树思路,超详细注释)

news2025/7/19 16:50:37

Needleman-Wunsch和Smith-Waterman算法py实现(多条回溯路径)

话不多说,直接上结果图,多条回溯路径。
NW结果

原理

在这里插入图片描述在这里插入图片描述

代码详解(以NW为例)

导入包以及参数设置

import numpy as np

sequence_1 = "AACGTACTCAAGTCT"
sequence_2 = "TCGTACTCTAACGAT"
match = 9
mismatch = -6
gap = -2

创建得分矩阵

  • 创建得分矩阵,行数为第一条序列长度加一,列数为第二条序列长度加二
  • 创建是否匹配的矩阵,这个矩阵的长宽就分别是两条序列的长度了。如果匹配了,对应的格子就是匹配的得分,反之就是不匹配的得分
  • 动态规划的思想算每个格子的得分,每个格子需要考虑其左、上、左上的值,也可以说是考虑序列一、序列二引入空缺或直接匹配的最大值
# 创建得分矩阵,行数为第一条序列长度加一,列数为第二条序列长度加二
Score = np.zeros((len(sequence_1) + 1, len(sequence_2) + 1))
# 创建是否匹配的矩阵,这个矩阵的长宽就分别是两条序列的长度了。如果匹配了,对应的格子就是匹配的得分,反之就是不匹配的得分
Match_or_not = np.zeros((len(sequence_1), len(sequence_2)))
for i in range(len(sequence_1)):
    for j in range(len(sequence_2)):
        if sequence_1[i] == sequence_2[j]:
            Match_or_not[i][j] = match
        else:
            Match_or_not[i][j] = mismatch

# 填得分矩阵
# 第一步:初始化第一行和第一列
for i in range(len(sequence_1) + 1):
    Score[i][0] = i * gap
for j in range(len(sequence_2) + 1):
    Score[0][j] = j * gap
# 第二步:动态规划的思想算每个格子的得分,每个格子需要考虑其左、上、左上的值,也可以说是考虑序列一、序列二引入空缺或直接匹配的最大值
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        Score[i][j] = max(Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1],
                          Score[i - 1][j] + gap,
                          Score[i][j - 1] + gap)

看看得分矩阵长啥样吧
在这里插入图片描述
请添加图片描述
请添加图片描述

回溯

我们需要考虑的是可能会有多条回溯路径。全局比对的回溯是从右下角开始,左上角结束,其中可能会有分叉点。我们可以把右下角看成是一个树的根,矩阵中的每个值看成是一个节点。每个节点都可能会有三个子节点:左,上,对角线。分别对应了回溯的方向。而整个回溯的过程也就是遍历这颗三叉树的过程,严谨的说是从根节点遍历每个叶子节点的过程。

# 开始回溯
'''
我们需要考虑的是可能会有多条回溯路径。
全局比对的回溯是从右下角开始,左上角结束,其中可能会有分叉点。
我们可以把右下角看成是一个树的根,矩阵中的每个值看成是一个节点。
每个节点都可能会有三个子节点:左,上,对角线。分别对应了回溯的方向。
而整个回溯的过程也就是遍历这颗三叉树的过程,严谨的说是从根节点遍历每个叶子节点的过程。
'''


class Node:
    # 用类来建立三叉树节点,属性包括了行、列、得分、左子树、上子树、对角线子树
    def __init__(self, row=None, col=None, score=None):
        self.row = row
        self.col = col
        self.score = score
        self.left = None
        self.up = None
        self.diag = None


def isLeaf(self):
    # 判断是否是叶子节点
    return self.left is None and self.up is None and self.diag is None
    # 递归的函数查找从根节点到每个叶节点的路径


# 回溯路径的个数、回溯路径中的行号和列号
traceback_pathway_number = 0
traceback_pathway_row = [[]]
traceback_pathway_col = [[]]


def SaveRootToLeafPaths(Node, path_row, path_col):
    # 如果没有子树了
    if Node is None:
        return
    # 包含当前节点的路径
    path_row.append(Node.row)
    path_col.append(Node.col)
    global traceback_pathway_number
    global traceback_pathway_row
    global traceback_pathway_col
    # 如果找到叶节点,保存路径
    if isLeaf(Node):
        if traceback_pathway_number == 0:
            traceback_pathway_row[traceback_pathway_number] = list(path_row)
            traceback_pathway_col[traceback_pathway_number] = list(path_col)
        else:
            traceback_pathway_row += [list(path_row)]
            traceback_pathway_col += [list(path_col)]
        traceback_pathway_number += 1
    # 递归左、上、对角子树
    SaveRootToLeafPaths(Node.left, path_row, path_col)
    SaveRootToLeafPaths(Node.up, path_row, path_col)
    SaveRootToLeafPaths(Node.diag, path_row, path_col)
    # 回溯,出栈
    path_row.pop()
    path_col.pop()


# 建立三叉树,为 Score 矩阵里所有值都找到它的左、上、对角子树,用一个二位列表来存储节点
NodeTree = [[Node() for _ in range(len(sequence_2) + 1)] for _ in range(len(sequence_1) + 1)]
# 先把节点们的行号列号和得分记录下来
for i in range(len(sequence_1) + 1):
    for j in range(len(sequence_2) + 1):
        NodeTree[i][j].row = i
        NodeTree[i][j].col = j
        NodeTree[i][j].score = Score[i][j]
# 设置第一列和第一行的节点的上子树和左子树(其实也能在下面这个大循环里设置,但是这样可读性更高)
for i in range(1, len(sequence_1) + 1):
    NodeTree[i][0].up = NodeTree[i - 1][0]
for j in range(1, len(sequence_2) + 1):
    NodeTree[0][j].left = NodeTree[0][j - 1]
# 设置剩下的节点
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        if (Score[i][j] == Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1]):
            NodeTree[i][j].diag = NodeTree[i - 1][j - 1]
        if (Score[i][j] == Score[i - 1][j] + gap):
            NodeTree[i][j].up = NodeTree[i - 1][j]
        if (Score[i][j] == Score[i][j - 1] + gap):
            NodeTree[i][j].left = NodeTree[i][j - 1]
# 遍历树并保存路径
SaveRootToLeafPaths(NodeTree[len(sequence_1)][len(sequence_2)], [], [])
# 改成numpy的ndarray类型,更加方便!
traceback_pathway_row = np.array(traceback_pathway_row)
traceback_pathway_col = np.array(traceback_pathway_col)
# 记录一下回溯时走不走左边或上边,如果走就记为1,不走就记为0
Go_left = traceback_pathway_col[:, range(traceback_pathway_col.shape[1] - 1)] - traceback_pathway_col[:, range(1,
                                                                                                               traceback_pathway_col.shape[
                                                                                                                   1])]
Go_up = traceback_pathway_row[:, range(traceback_pathway_row.shape[1] - 1)] - traceback_pathway_row[:,
                                                                              range(1, traceback_pathway_row.shape[1])]
# 用列表来存储序列一和序列二比对后的结果
seq1_align_set = []
seq2_align_set = []
print("总共有{}个比对结果".format(traceback_pathway_number))
for tpn in range(traceback_pathway_number):
    '''
    下面其实就是经典的nw回溯的代码了,这部分的原理可以参考nw算法回溯的伪代码。
    唯一不同的就是我们是多条回溯路径,所以有多少条路经就得循环多少次。
    值得一提的是,回溯过去的序列是逆序的,
    在python中字符串逆置十分方便,只需要合理利用切片,如:str[::-1]即可。
    '''
    seq1_align = ''
    seq2_align = ''
    i = len(sequence_1)
    j = len(sequence_2)
    k = 0
    while i > 0 or j > 0:
        if i > 0 and j > 0 and Go_left[tpn][k] and Go_up[tpn][k]:
            seq1_align += sequence_1[i - 1]
            seq2_align += sequence_2[j - 1]
            i -= 1
            j -= 1
        elif i > 0 and not (Go_left[tpn][k]) and Go_up[tpn][k]:
            seq1_align += sequence_1[i - 1]
            seq2_align += '-'
            i -= 1
        elif j > 0 and Go_left[tpn][k] and not (Go_up[tpn][k]):
            seq1_align += '-'
            seq2_align += sequence_2[j - 1]
            j -= 1
        k += 1
    seq1_align_set += [seq1_align[::-1]]
    seq2_align_set += [seq2_align[::-1]]
    print("下面是第{}个".format(tpn + 1))
    print(seq1_align[::-1])
    print(seq2_align[::-1])
    print(' ')
输出

总共有15个比对结果
下面是第1个
AA-CGTACTC-AA-G-TCT
–TCGTACTCTAACGAT–

下面是第2个
A-ACGTACTC-AA-G-TCT
-T-CGTACTCTAACGAT–

下面是第3个
-AACGTACTC-AA-G-TCT
T–CGTACTCTAACGAT–

下面是第4个
AA-CGTACTC-AAGTC–T
–TCGTACTCTAA–CGAT

下面是第5个
A-ACGTACTC-AAGTC–T
-T-CGTACTCTAA–CGAT

下面是第6个
-AACGTACTC-AAGTC–T
T–CGTACTCTAA–CGAT

下面是第7个
AA-CGTACTC-AA-GTC-T
–TCGTACTCTAACG–AT

下面是第8个
A-ACGTACTC-AA-GTC-T
-T-CGTACTCTAACG–AT

下面是第9个
-AACGTACTC-AA-GTC-T
T–CGTACTCTAACG–AT

下面是第10个
AA-CGTACTC-AA-GT-CT
–TCGTACTCTAACG-A-T

下面是第11个
A-ACGTACTC-AA-GT-CT
-T-CGTACTCTAACG-A-T

下面是第12个
-AACGTACTC-AA-GT-CT
T–CGTACTCTAACG-A-T

下面是第13个
AA-CGTACTC-AA-G-TCT
–TCGTACTCTAACGA–T

下面是第14个
A-ACGTACTC-AA-G-TCT
-T-CGTACTCTAACGA–T

下面是第15个
-AACGTACTC-AA-G-TCT
T–CGTACTCTAACGA–T

画一下回溯的路径

请添加图片描述
请添加图片描述

可视化代码

'''
下面就是得分矩阵的热图以及回溯路径(格子)画出来了
'''
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt

Score = pd.DataFrame(Score)
row_name = list(sequence_1)
row_name.insert(0, ' ')
col_name = list(sequence_2)
col_name.insert(0, ' ')
Score.index = row_name
Score.columns = col_name
traceback_way_mat = np.ones([len(sequence_1) + 1, len(sequence_2) + 1])

for i in range(traceback_pathway_row.shape[0]):
    traceback_way_mat[traceback_pathway_row[i][:], traceback_pathway_col[i][:]] = 0
ax1 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", annot=True)
plt.savefig('nw_Heatmap with annotation.png',dpi=300)
plt.show()
ax2 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r")
plt.savefig('nw_Heatmap.png',dpi=300)
plt.show()
ax3 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", mask=traceback_way_mat)
plt.savefig('nw_Heatmap_traceback',dpi=300)
plt.show()

#%%
# params={'font.family':'serif',
#         'font.serif':'Times New Roman',
#         'font.style':'normal',#'italic'
#         'font.weight':'normal', #or 'blod'
#         'font.size':12,#or large,small
#         'figure.figsize':(6,6)
#         }
plt.rcParams['figure.figsize'] = (6, 6)
# plt.rcParams.update(params)
for j in range(traceback_pathway_col.shape[0]):
    fig = plt.figure()

    ax = plt.axes()
    plt.grid(zorder=0, linestyle='-.')
    for i in range(traceback_pathway_col.shape[1]-1):
        xs = traceback_pathway_col[j][i]
        ys = traceback_pathway_row[j][i]
        xe = traceback_pathway_col[j][i+1]
        ye = traceback_pathway_row[j][i+1]
        ax.arrow(xs, ys, xe-xs, ye-ys, length_includes_head=True,head_width=0.3, fc='crimson', ec='hotpink',zorder=10)

    ax.set_xlim(-0.5, len(col_name)-0.5)
    ax.set_ylim(-0.5, len(col_name)-0.5)
    plt.xticks(np.arange(0,len(col_name),1), col_name)
    plt.yticks(np.arange(0,len(row_name),1), row_name)
    ax.xaxis.set_ticks_position('top')
    ax.invert_yaxis()
    ax.set_title('No.{}'.format(j+1),fontsize=12,color='k',loc='left',y=0.86,x=0.3,fontweight='bold')
    ax.set_title('{}{}{}'.format(seq1_align_set[j],'\n',seq2_align_set[j]),fontsize=16,fontfamily ='monospace',color='k',fontweight='bold',y=0.83,x=0.7)
    # plt.rcParams['figure.figsize'] = (6, 6)
    plt.savefig('nw_No.{}'.format(j+1)+'.png',dpi=300)
    plt.show()

Smith Waterman算法

局部比对的和全局比对差不多,只需再几个小细节上改改就行,大家可以在两个代码之间找找茬~

完整代码

NW

import numpy as np

sequence_1 = "AACGTACTCAAGTCT"
sequence_2 = "TCGTACTCTAACGAT"
match = 9
mismatch = -6
gap = -2

# 创建得分矩阵,行数为第一条序列长度加一,列数为第二条序列长度加二
Score = np.zeros((len(sequence_1) + 1, len(sequence_2) + 1))
# 创建是否匹配的矩阵,这个矩阵的长宽就分别是两条序列的长度了。如果匹配了,对应的格子就是匹配的得分,反之就是不匹配的得分
Match_or_not = np.zeros((len(sequence_1), len(sequence_2)))
for i in range(len(sequence_1)):
    for j in range(len(sequence_2)):
        if sequence_1[i] == sequence_2[j]:
            Match_or_not[i][j] = match
        else:
            Match_or_not[i][j] = mismatch

# 填得分矩阵
# 第一步:初始化第一行和第一列
for i in range(len(sequence_1) + 1):
    Score[i][0] = i * gap
for j in range(len(sequence_2) + 1):
    Score[0][j] = j * gap
# 第二步:动态规划的思想算每个格子的得分,每个格子需要考虑其左、上、左上的值,也可以说是考虑序列一、序列二引入空缺或直接匹配的最大值
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        Score[i][j] = max(Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1],
                          Score[i - 1][j] + gap,
                          Score[i][j - 1] + gap)

# 开始回溯
'''
我们需要考虑的是可能会有多条回溯路径。
全局比对的回溯是从右下角开始,左上角结束,其中可能会有分叉点。
我们可以把右下角看成是一个树的根,矩阵中的每个值看成是一个节点。
每个节点都可能会有三个子节点:左,上,对角线。分别对应了回溯的方向。
而整个回溯的过程也就是遍历这颗三叉树的过程,严谨的说是从根节点遍历每个叶子节点的过程。
'''


class Node:
    # 用类来建立三叉树节点,属性包括了行、列、得分、左子树、上子树、对角线子树
    def __init__(self, row=None, col=None, score=None):
        self.row = row
        self.col = col
        self.score = score
        self.left = None
        self.up = None
        self.diag = None


def isLeaf(self):
    # 判断是否是叶子节点
    return self.left is None and self.up is None and self.diag is None
    # 递归的函数查找从根节点到每个叶节点的路径


# 回溯路径的个数、回溯路径中的行号和列号
traceback_pathway_number = 0
traceback_pathway_row = [[]]
traceback_pathway_col = [[]]


def SaveRootToLeafPaths(Node, path_row, path_col):
    # 如果没有子树了
    if Node is None:
        return
    # 包含当前节点的路径
    path_row.append(Node.row)
    path_col.append(Node.col)
    global traceback_pathway_number
    global traceback_pathway_row
    global traceback_pathway_col
    # 如果找到叶节点,保存路径
    if isLeaf(Node):
        if traceback_pathway_number == 0:
            traceback_pathway_row[traceback_pathway_number] = list(path_row)
            traceback_pathway_col[traceback_pathway_number] = list(path_col)
        else:
            traceback_pathway_row += [list(path_row)]
            traceback_pathway_col += [list(path_col)]
        traceback_pathway_number += 1
    # 递归左、上、对角子树
    SaveRootToLeafPaths(Node.left, path_row, path_col)
    SaveRootToLeafPaths(Node.up, path_row, path_col)
    SaveRootToLeafPaths(Node.diag, path_row, path_col)
    # 回溯,出栈
    path_row.pop()
    path_col.pop()


# 建立三叉树,为 Score 矩阵里所有值都找到它的左、上、对角子树,用一个二位列表来存储节点
NodeTree = [[Node() for _ in range(len(sequence_2) + 1)] for _ in range(len(sequence_1) + 1)]
# 先把节点们的行号列号和得分记录下来
for i in range(len(sequence_1) + 1):
    for j in range(len(sequence_2) + 1):
        NodeTree[i][j].row = i
        NodeTree[i][j].col = j
        NodeTree[i][j].score = Score[i][j]
# 设置第一列和第一行的节点的上子树和左子树(其实也能在下面这个大循环里设置,但是这样可读性更高)
for i in range(1, len(sequence_1) + 1):
    NodeTree[i][0].up = NodeTree[i - 1][0]
for j in range(1, len(sequence_2) + 1):
    NodeTree[0][j].left = NodeTree[0][j - 1]
# 设置剩下的节点
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        if (Score[i][j] == Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1]):
            NodeTree[i][j].diag = NodeTree[i - 1][j - 1]
        if (Score[i][j] == Score[i - 1][j] + gap):
            NodeTree[i][j].up = NodeTree[i - 1][j]
        if (Score[i][j] == Score[i][j - 1] + gap):
            NodeTree[i][j].left = NodeTree[i][j - 1]
# 遍历树并保存路径
SaveRootToLeafPaths(NodeTree[len(sequence_1)][len(sequence_2)], [], [])
# 改成numpy的ndarray类型,更加方便!
traceback_pathway_row = np.array(traceback_pathway_row)
traceback_pathway_col = np.array(traceback_pathway_col)
# 记录一下回溯时走不走左边或上边,如果走就记为1,不走就记为0
Go_left = traceback_pathway_col[:, range(traceback_pathway_col.shape[1] - 1)] - traceback_pathway_col[:, range(1,
                                                                                                               traceback_pathway_col.shape[
                                                                                                                   1])]
Go_up = traceback_pathway_row[:, range(traceback_pathway_row.shape[1] - 1)] - traceback_pathway_row[:,
                                                                              range(1, traceback_pathway_row.shape[1])]
# 用列表来存储序列一和序列二比对后的结果
seq1_align_set = []
seq2_align_set = []
print("总共有{}个比对结果".format(traceback_pathway_number))
for tpn in range(traceback_pathway_number):
    '''
    下面其实就是经典的nw回溯的代码了,这部分的原理可以参考nw算法回溯的伪代码。
    唯一不同的就是我们是多条回溯路径,所以有多少条路经就得循环多少次。
    值得一提的是,回溯过去的序列是逆序的,
    在python中字符串逆置十分方便,只需要合理利用切片,如:str[::-1]即可。
    '''
    seq1_align = ''
    seq2_align = ''
    i = len(sequence_1)
    j = len(sequence_2)
    k = 0
    while i > 0 or j > 0:
        if i > 0 and j > 0 and Go_left[tpn][k] and Go_up[tpn][k]:
            seq1_align += sequence_1[i - 1]
            seq2_align += sequence_2[j - 1]
            i -= 1
            j -= 1
        elif i > 0 and not (Go_left[tpn][k]) and Go_up[tpn][k]:
            seq1_align += sequence_1[i - 1]
            seq2_align += '-'
            i -= 1
        elif j > 0 and Go_left[tpn][k] and not (Go_up[tpn][k]):
            seq1_align += '-'
            seq2_align += sequence_2[j - 1]
            j -= 1
        k += 1
    seq1_align_set += [seq1_align[::-1]]
    seq2_align_set += [seq2_align[::-1]]
    print("下面是第{}个".format(tpn + 1))
    print(seq1_align[::-1])
    print(seq2_align[::-1])
    print(' ')
#%%
'''
下面就是得分矩阵的热图以及回溯路径(格子)画出来了
'''
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt

Score = pd.DataFrame(Score)
row_name = list(sequence_1)
row_name.insert(0, ' ')
col_name = list(sequence_2)
col_name.insert(0, ' ')
Score.index = row_name
Score.columns = col_name
traceback_way_mat = np.ones([len(sequence_1) + 1, len(sequence_2) + 1])

for i in range(traceback_pathway_row.shape[0]):
    traceback_way_mat[traceback_pathway_row[i][:], traceback_pathway_col[i][:]] = 0
ax1 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", annot=True)
plt.savefig('nw_Heatmap with annotation.png',dpi=300)
plt.show()
ax2 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r")
plt.savefig('nw_Heatmap.png',dpi=300)
plt.show()
ax3 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", mask=traceback_way_mat)
plt.savefig('nw_Heatmap_traceback',dpi=300)
plt.show()

#%%
# params={'font.family':'serif',
#         'font.serif':'Times New Roman',
#         'font.style':'normal',#'italic'
#         'font.weight':'normal', #or 'blod'
#         'font.size':12,#or large,small
#         'figure.figsize':(6,6)
#         }
plt.rcParams['figure.figsize'] = (6, 6)
# plt.rcParams.update(params)
for j in range(traceback_pathway_col.shape[0]):
    fig = plt.figure()

    ax = plt.axes()
    plt.grid(zorder=0, linestyle='-.')
    for i in range(traceback_pathway_col.shape[1]-1):
        xs = traceback_pathway_col[j][i]
        ys = traceback_pathway_row[j][i]
        xe = traceback_pathway_col[j][i+1]
        ye = traceback_pathway_row[j][i+1]
        ax.arrow(xs, ys, xe-xs, ye-ys, length_includes_head=True,head_width=0.3, fc='crimson', ec='hotpink',zorder=10)

    ax.set_xlim(-0.5, len(col_name)-0.5)
    ax.set_ylim(-0.5, len(col_name)-0.5)
    plt.xticks(np.arange(0,len(col_name),1), col_name)
    plt.yticks(np.arange(0,len(row_name),1), row_name)
    ax.xaxis.set_ticks_position('top')
    ax.invert_yaxis()
    ax.set_title('No.{}'.format(j+1),fontsize=12,color='k',loc='left',y=0.86,x=0.3,fontweight='bold')
    ax.set_title('{}{}{}'.format(seq1_align_set[j],'\n',seq2_align_set[j]),fontsize=16,fontfamily ='monospace',color='k',fontweight='bold',y=0.83,x=0.7)
    # plt.rcParams['figure.figsize'] = (6, 6)
    plt.savefig('nw_No.{}'.format(j+1)+'.png',dpi=300)
    plt.show()

import datetime
print("这是代码执行时间: ",datetime.datetime.now())


SW

import numpy as np

sequence_1 = "AACGTACTCAAGTCT"
sequence_2 = "TCGTACTCTAACGAT"
match = 9
mismatch = -6
gap = -2

# 创建得分矩阵,行数为第一条序列长度加一,列数为第二条序列长度加二
Score = np.zeros((len(sequence_1) + 1, len(sequence_2) + 1))
# 创建是否匹配的矩阵,这个矩阵的长宽就分别是两条序列的长度了。如果匹配了,对应的格子就是匹配的得分,反之就是不匹配的得分
Match_or_not = np.zeros((len(sequence_1), len(sequence_2)))
for i in range(len(sequence_1)):
    for j in range(len(sequence_2)):
        if sequence_1[i] == sequence_2[j]:
            Match_or_not[i][j] = match
        else:
            Match_or_not[i][j] = mismatch

# 填得分矩阵
# 第一步:初始化第一行和第一列
for i in range(len(sequence_1) + 1):
    Score[i][0] = 0
for j in range(len(sequence_2) + 1):
    Score[0][j] = 0
# 第二步:动态规划的思想算每个格子的得分,每个格子需要考虑其左、上、左上的值,也可以说是考虑序列一、序列二引入空缺或直接匹配的最大值
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        Score[i][j] = max(Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1],
                          Score[i - 1][j] + gap,
                          Score[i][j - 1] + gap, 0)

# 开始回溯
'''
我们需要考虑的是可能会有多条回溯路径。
全局比对的回溯是从右下角开始,左上角结束,其中可能会有分叉点。
我们可以把右下角看成是一个树的根,矩阵中的每个值看成是一个节点。
每个节点都可能会有三个子节点:左,上,对角线。分别对应了回溯的方向。
而整个回溯的过程也就是遍历这颗三叉树的过程,严谨的说是从根节点遍历每个叶子节点的过程。
'''


class Node:
    # 用类来建立三叉树节点,属性包括了行、列、得分、左子树、上子树、对角线子树
    def __init__(self, row=None, col=None, score=None):
        self.row = row
        self.col = col
        self.score = score
        self.left = None
        self.up = None
        self.diag = None


def isLeaf(self):
    # 判断是否是叶子节点
    return self.left is None and self.up is None and self.diag is None
    # 递归的函数查找从根节点到每个叶节点的路径


# 回溯路径的个数、回溯路径中的行号和列号
traceback_pathway_number = 0
traceback_pathway_row = [[]]
traceback_pathway_col = [[]]


def SaveRootToLeafPaths(Node, path_row, path_col):
    # 如果没有子树了
    if Node is None:
        return
    # 包含当前节点的路径
    path_row.append(Node.row)
    path_col.append(Node.col)
    global traceback_pathway_number
    global traceback_pathway_row
    global traceback_pathway_col
    # 如果找到叶节点,保存路径
    if isLeaf(Node):
        if traceback_pathway_number == 0:
            traceback_pathway_row[traceback_pathway_number] = list(path_row)
            traceback_pathway_col[traceback_pathway_number] = list(path_col)
        else:
            traceback_pathway_row += [list(path_row)]
            traceback_pathway_col += [list(path_col)]
        traceback_pathway_number += 1
    # 递归左、上、对角子树
    SaveRootToLeafPaths(Node.left, path_row, path_col)
    SaveRootToLeafPaths(Node.up, path_row, path_col)
    SaveRootToLeafPaths(Node.diag, path_row, path_col)
    # 回溯,出栈
    path_row.pop()
    path_col.pop()


# 建立三叉树,为 Score 矩阵里所有值都找到它的左、上、对角子树,用一个二位列表来存储节点
NodeTree = [[Node() for _ in range(len(sequence_2) + 1)] for _ in range(len(sequence_1) + 1)]
# 先把节点们的行号列号和得分记录下来
for i in range(len(sequence_1) + 1):
    for j in range(len(sequence_2) + 1):
        NodeTree[i][j].row = i
        NodeTree[i][j].col = j
        NodeTree[i][j].score = Score[i][j]
# 设置第一列和第一行的节点的上子树和左子树(其实也能在下面这个大循环里设置,但是这样可读性更高)
for i in range(1, len(sequence_1) + 1):
    NodeTree[i][0].up = NodeTree[i - 1][0]
for j in range(1, len(sequence_2) + 1):
    NodeTree[0][j].left = NodeTree[0][j - 1]
# 设置剩下的节点
for i in range(1, len(sequence_1) + 1):
    for j in range(1, len(sequence_2) + 1):
        if (Score[i][j] == Score[i - 1][j - 1] + Match_or_not[i - 1][j - 1]):
            NodeTree[i][j].diag = NodeTree[i - 1][j - 1]
        if (Score[i][j] == Score[i - 1][j] + gap):
            NodeTree[i][j].up = NodeTree[i - 1][j]
        if (Score[i][j] == Score[i][j - 1] + gap):
            NodeTree[i][j].left = NodeTree[i][j - 1]
# 遍历树并保存路径
r, c = np.where(Score == np.max(Score))
SaveRootToLeafPaths(NodeTree[int(r)][int(c)], [], [])
# 改成numpy的ndarray类型,更加方便!
traceback_pathway_row = np.array(traceback_pathway_row)
traceback_pathway_col = np.array(traceback_pathway_col)
# 记录一下回溯时走不走左边或上边,如果走就记为1,不走就记为0
Go_left = traceback_pathway_col[:, range(traceback_pathway_col.shape[1] - 1)] - traceback_pathway_col[:, range(1,
                                                                                                               traceback_pathway_col.shape[
                                                                                                                   1])]
Go_up = traceback_pathway_row[:, range(traceback_pathway_row.shape[1] - 1)] - traceback_pathway_row[:,
                                                                              range(1, traceback_pathway_row.shape[1])]
# 用列表来存储序列一和序列二比对后的结果
seq1_align_set = []
seq2_align_set = []
print("总共有{}个比对结果".format(traceback_pathway_number))
for tpn in range(traceback_pathway_number):
    '''
    下面其实就是经典的nw回溯的代码了,这部分的原理可以参考nw算法回溯的伪代码。
    唯一不同的就是我们是多条回溯路径,所以有多少条路经就得循环多少次。
    值得一提的是,回溯过去的序列是逆序的,
    在python中字符串逆置十分方便,只需要合理利用切片,如:str[::-1]即可。
    '''
    seq1_align = ''
    seq2_align = ''
    i = int(r)
    j = int(c)
    k = 0
    while Score[i][j] > 0:
        # waterman修改条件,到零结束
        if k < traceback_pathway_col.shape[1] - 1:
            if Go_left[tpn][k] and Go_up[tpn][k]:
                seq1_align += sequence_1[i - 1]
                seq2_align += sequence_2[j - 1]
                i -= 1
                j -= 1
            elif not (Go_left[tpn][k]) and Go_up[tpn][k]:
                seq1_align += sequence_1[i - 1]
                seq2_align += '-'
                i -= 1
            elif Go_left[tpn][k] and not (Go_up[tpn][k]):
                seq1_align += '-'
                seq2_align += sequence_2[j - 1]
                j -= 1
            k += 1
    seq1_align_set += [seq1_align[::-1]]
    seq2_align_set += [seq2_align[::-1]]
    print("下面是第{}个".format(tpn + 1))
    print(seq1_align[::-1])
    print(seq2_align[::-1])
    print(' ')
#%%
'''
下面就是得分矩阵的热图以及回溯路径(格子)画出来了
'''
import pandas as pd
import seaborn as sns
from matplotlib import pyplot as plt

Score = pd.DataFrame(Score)
row_name = list(sequence_1)
row_name.insert(0, ' ')
col_name = list(sequence_2)
col_name.insert(0, ' ')
Score.index = row_name
Score.columns = col_name
traceback_way_mat = np.ones([len(sequence_1) + 1, len(sequence_2) + 1])

for i in range(traceback_pathway_row.shape[0]):
    traceback_way_mat[traceback_pathway_row[i][:], traceback_pathway_col[i][:]] = 0
ax1 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", annot=True)
plt.savefig('sw_Heatmap with annotation.png',dpi=300)
plt.show()
ax2 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r")
plt.savefig('sw_Heatmap.png',dpi=300)
plt.show()
ax3 = sns.heatmap(Score, linecolor='white', linewidth=0, square=True, cmap="RdBu_r", mask=traceback_way_mat)
plt.savefig('sw_Heatmap_traceback',dpi=300)
plt.show()

#%%
plt.rcParams['figure.figsize'] = (6, 6)
for j in range(traceback_pathway_col.shape[0]):
    fig = plt.figure()
    ax = plt.axes()
    plt.grid(zorder=0, linestyle='-.')
    for i in range(traceback_pathway_col.shape[1]-2):
        xs = traceback_pathway_col[j][i]
        ys = traceback_pathway_row[j][i]
        xe = traceback_pathway_col[j][i+1]
        ye = traceback_pathway_row[j][i+1]
        ax.arrow(xs, ys, xe-xs, ye-ys, length_includes_head=True,head_width=0.3, fc='crimson', ec='hotpink',zorder=10)
    ax.set_xlim(-0.5, len(col_name)-0.5)
    ax.set_ylim(-0.5, len(col_name)-0.5)
    plt.xticks(np.arange(0,len(col_name),1), col_name)
    plt.yticks(np.arange(0,len(row_name),1), row_name)
    ax.xaxis.set_ticks_position('top')
    ax.invert_yaxis()
    # ax.set_title('No.{}'.format(j+1),fontsize=12,color='k',loc='left')
    ax.set_title('No.{}'.format(j + 1), fontsize=12, color='k', loc='left', y=0.86, x=0.38, fontweight='bold')
    ax.set_title('{}{}{}'.format(seq1_align_set[j], '\n', seq2_align_set[j]), fontsize=16, fontfamily='monospace',
                 color='k', fontweight='bold', y=0.83, x=0.7)
    plt.savefig('sw_No.{}'.format(j+1)+'.png',dpi=300)
    plt.show()

import datetime
print("这是代码执行时间: ",datetime.datetime.now())

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/17341.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析经典算法——红黑树

数据分析经典算法——红黑树红黑树的重要性红黑树的定义红黑树图解红黑树的重要性 红黑树的优势 红黑树能够以O(log2(N))的时间复杂度的时间复杂度进行搜索、插入、删除操作。 此外,任何不平衡都会在3次旋转之内解决。 这一点是AVL所不具备的。 而且实际应用中&#xff0c;很多…

JAVA concurrency -- AQS 源码详解

概述 AQS全称AbstractQueuedSynchronizer是 jdk 中一个非常重要的方法&#xff0c;这是一个 jdk 的同步器的实现&#xff0c;JUC 中的很多类例如ReentrantLock等的实现都依赖于 AQS。 CAS AQS 的同步实现方式依赖于 CAS&#xff0c;那么 CAS 究竟是什么呢&#xff1f; CAS全…

写了半个月近3万字,助你直接上手Flink,原来这就是流批一体的处理方式

Flink即刻出发1.1.Flink 数据流1.2.Flink 分层 API1.3.Flink流处理程序的一般流程1.4.搭建Flink工程1.4.1.创建Maven项目1.5.批处理的单词统计1.5.1.示例1.5.2.开发步骤1.5.3.参考代码1.6.流处理的单词统计1.6.1.示例1.6.2.开发步骤1.6.3. 参考代码&#xff1a;java语言实现1.6…

Vue学习

Vue学习(第一天) 1、Vue.js安装 1.创建vue项目 2.启动vue项目 3.vue的MVVM 2、vue学习-1 1.vue cli 1.什么是vue cli 2.vue cli使用前提-Node 3.vue cli使用前提-Webpack 4.vue cli的使用 5.认识vue cli3 6.目录结构 7.vue ui 项目管理工具 2.什么是路由 1.前端阶段 3.url和hi…

C++STL——string类与模拟实现

STL容器——string类什么是STLstring类字符串的标准什么是stringstring常用接口介绍string的初始化比较大小与赋值容量对象的修改访问及遍历操作string中的swap与C库中的swap的区别非成员函数string类的模拟实现深浅拷贝与现代写法什么是STL STL(standard template libaray-标…

WRFV3.8.1编译报错,无法显示exe文件

问题报错&#xff1a;在WRF中遇到了一个可能和ubuntu系统有关的报错&#xff0c;主要表现为random seed过小&#xff0c;找不到&#xff0c;无法进行compile&#xff0c;导致compile em_real后无法生成4个*.exe文件。第一个报错出现位置为&#xff1a;。附件为compile.log。 图…

【树莓派不吃灰】命令篇④ Linux 常用命令学习

目录1. 常用命令1.1 操作文件及目录1.2 系统常用命令1.3 压缩解压缩1.4 linux系统常用快捷键及符号命令2. Linux 命令大全❤️ 博客主页 单片机菜鸟哥&#xff0c;一个野生非专业硬件IOT爱好者 ❤️❤️ 本篇创建记录 2022-11-18 ❤️❤️ 本篇更新记录 2022-11-18 ❤️&#x…

YOLO系列改进之四十四——融入适配GPU的轻量级 G-GhostNet

文章目录前言一、解决问题二、基本原理三、​添加方法总结前言 作为当前先进的深度学习目标检测算法YOLOv7&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列文章…

Adafruit_GFX matrix ws2812像素屏库使用教程AWTRIX2.0像素时钟

AWTRIX2.0像素时钟很炫酷但必须要与服务器配合使用。这个库可以做自己的点阵时钟离线版。想怎么玩就怎么玩不受服务器牵绊。 第一步&#xff1a;下载mixy库然后倒入&#xff0c;必须有以下库文件&#xff1a; Adafruit_GFX FastLED FastLED_NeoMatrix TomThumb #include <Li…

Seata 1.5.2 源码学习(Client端)

在上一篇中通过阅读Seata服务端的代码&#xff0c;我们了解到TC是如何处理来自客户端的请求的&#xff0c;今天这一篇一起来了解一下客户端是如何处理TC发过来的请求的。要想搞清楚这一点&#xff0c;还得从GlobalTransactionScanner说起。 启动的时候&#xff0c;会调用Global…

【计算机毕业设计】新冠疫情隔离人员信息管理系统+vue源码

一、系统截图&#xff08;需要演示视频可以私聊&#xff09; 摘 要 网络的广泛应用给生活带来了十分的便利。所以把基于小程序的社区疫情防控管理与现在网络相结合&#xff0c;利用ssm框架技术建设基于小程序的社区疫情防控系统&#xff0c;实现基于小程序的社区疫情防控的信息…

双线路捆绑

双线路捆绑是在服务器上接入两条上网线路并行使用 以达到提高链路上下行带宽&#xff08;即上传和下载速度&#xff09;的目的 默认情况下双线路捆绑采用负载均衡模式&#xff0c;并可更改为互为备份模式。 在负载均衡模式下&#xff0c;双线路的使用是基于会话的&#xff0…

已经有 MESI 协议,为什么还需要 volatile 关键字?

本文已收录到 GitHub AndroidFamily&#xff0c;有 Android 进阶知识体系&#xff0c;欢迎 Star。技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 进 Android 面试交流群。 前言 大家好&#xff0c;我是小彭。 在上一篇文章里&#xff0c;我们聊到了 CPU 的缓存一致性问…

树莓派使用docker搭建owncloud私有云--外挂硬盘

一&#xff0e;安装docker 1. 一键脚本&#xff1a; sudo curl -sSL https://get.docker.com | sh2. 查看docker是否安装成功 docker -v出现版本号即为成功 二&#xff0e;每次开机自动挂载硬盘到树莓派 sudo nano /etc/fstab在最后一行加入挂载信息 /dev/sda1 /home/pi/…

[附源码]java毕业设计农村政务管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

[附源码]SSM计算机毕业设计智慧农业销售平台JAVA

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

多进程编程 VS 多线程编程

目录 一、进程 & 线程 二、进程与线程的优劣势 三、在什么场景下需要使用多进程编程&#xff1f; 进程也可以称为是“任务”。操作系统要想执行一个具体的“动作”&#xff0c;就需要创建出一个对应的进程。 一个程序在没有运行的时候&#xff0c;它仅仅是一个“可执行…

RHCE学习 --- 第六次作业

RHCE学习 — 第六次作业 首先要先装DNS服务器需要的包 [rootlocalhost ~]# yum install bind -y然后开始配置DNS服务 配置文件位置在/etc/named.conf下&#xff0c;建议先备份 注&#xff1a;备份的时候要cp -a&#xff0c;否则所属组会变&#xff0c;导致文件不可用 然后编辑…

WinForm,可能是Windows上手最快的图形框架了

文章目录Label和控件属性按钮和回调逻辑事件常用控件Label和控件属性 WinForm是一门非常经济实惠的技术&#xff0c;就是说&#xff0c;可以在短时间内学会&#xff0c;并迅速借此进行项目开发。尽管在很多方面不够现代&#xff0c;做出来的东西又Low又丑&#xff0c;但绝大多…

Redis的优惠券秒杀问题(六)超卖问题、一人一单问题

Redis的优惠券秒杀问题&#xff08;六&#xff09;超卖问题、一人一单问题 超卖问题 问题描述 使用Jmeter进行压测 发生超卖问题原因分析 解决方案 悲观锁与乐观锁 1. 版本号 2. CAS法 CAS三大问题&#xff08;题外话&#xff01;&#xff09; CAS三大问题的解…