从 Python 中的等长字符串计算 similarity/difference 矩阵

Calculating a similarity/difference matrix from equal length strings in Python

我在 Python 中有成对的等长字符串和一个可接受的字母表。并非字符串中的所有字母都来自公认的字母表。例如

str1 = 'ACGT-N?A'
str2 = 'AAGAA??T'
alphabet = 'ACGT'

我要得到的是一个numpy矩阵,描述了字符串之间的异同。 IE。矩阵的列是 str1 中接受的字母表中的字母,行是 str2 中接受的字母表中的字母。这些条目是 str1str2 包含相关字母的次数的总和。我只关心两个字符串都在给定位置接受字母的情况。

因此,对于上面的示例,我的输出将是(列为 str1,行为 str2,名称按字母顺序从左上角开始):

# cols & rows both refer to 'A', 'C', 'G', 'T' starting top left
# cols are str1, rows are str2

array([[ 1,  1,  0,  1],
       [ 0,  0,  0,  0],
       [ 0,  0,  1,  0],
       [ 1,  0,  0,  0]])

我可以通过遍历每对可能的解决方案来强行解决这个问题,但我想知道是否有人对更通用的解决方案有提示(或链接)。例如。有没有一种方法可以更接近我可以定义 N 个唯一字符的字母表,并在给定两个等长输入字符串的情况下得到一个 N×N 矩阵?

蛮力方法:

def matrix(s1,s2):
    m= np.zeros((4,4))
    for i in range(len(s1)):
        if s1[i]==s2[i]:
            if s1[i]=="A":
                m[0,0]=m[0,0]+1
            elif s1[i]=="C":
                m[1,1]=m[1,1]+1
            elif s1[i]=="G":
                m[2,2]=m[2,2]+1
            elif s1[i]=="T":
                m[3,3]=m[3,3]+1
        elif s1[i]=="A":
            if s2[i]=="C":
                m[1,0]=m[1,0]+1
            elif s2[i]=="G":
                m[2,0]=m[2,0]+1
            elif s2[i]=="T":
                m[3,0]=m[3,0]+1
        elif s1[i]=="C":
            if s2[i]=="A":
                m[0,1]=m[0,1]+1
            elif s2[i]=="G":
                m[2,1]=m[2,1]+1
            elif s2[i]=="T":
                m[3,1]=m[3,1]+1
        elif s1[i]=="G":
            if s2[i]=="A":
                m[0,2]=m[0,2]+1
            elif s2[i]=="C":
                m[1,2]=m[1,2]+1
            elif s2[i]=="T":
                m[3,2]=m[3,2]+1
        elif s1[i]=="T":
            if s2[i]=="C":
                m[1,3]=m[1,3]+1
            elif s2[i]=="G":
                m[2,3]=m[2,3]+1
            elif s2[i]=="A":
                m[0,3]=m[0,3]+1           
    return m

使用 set、一些理解和 pandas.DataFrame 可以简洁地完成此任务:

代码:

from collections import Counter
import pandas as pd

def dot_product(allowed, s1, s2):
    in_s1 = {c: set([y.start() for y in [
        x for x in re.finditer(c, s1)]]) for c in allowed}
    in_s2 = {c: set([y.start() for y in [
        x for x in re.finditer(c, s2)]]) for c in allowed}
    return pd.DataFrame(
        [[len(in_s1[c1] & in_s2[c2]) for c1 in allowed] for c2 in allowed],
        columns=list(allowed),
        index=list(allowed),
    )

测试代码:

str1 = 'ACGT-N?A'
str2 = 'AAGAA??T'
alphabet = 'ACGT'

print(dot_product_sum(alphabet, str1, str2))

结果:

   A  C  G  T
A  1  1  0  1
C  0  0  0  0
G  0  0  1  0
T  1  0  0  0

使用布尔矩阵的点积(保持顺序正确的最简单方法):

def simMtx(a, x, y):
    a = np.array(list(a))
    x = np.array(list(x))
    y = np.array(list(y))
    ax = (x[:, None] == a[None, :]).astype(int)
    ay = (y[:, None] == a[None, :]).astype(int)
    return np.dot(ay.T, ax)

simMtx(alphabet, str1, str2)
Out[183]: 
array([[1, 1, 0, 1],
       [0, 0, 0, 0],
       [0, 0, 1, 0],
       [1, 0, 0, 0]])