【问题标题】:How can I merge overlapping strings in python?如何在 python 中合并重叠的字符串?
【发布时间】:2018-04-30 05:37:20
【问题描述】:

我有一些字符串,

['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV']

这些字符串部分相互重叠。如果你手动重叠它们,你会得到:

SGALWDVPSPV

我想要一种从重叠字符串列表到 python 中最终压缩字符串的方法。我觉得这一定是有人已经解决的问题,并且正在努力避免重新发明轮子。我现在可以想象的方法要么是蛮力,要么是通过使用 biopython 和序列比对器变得比我想要的更复杂。我有一些简单的短字符串,只想以简单的方式正确合并它们。

有人对在 python 中执行此操作的好方法有任何建议吗?谢谢!

【问题讨论】:

  • ABC, B, ABC 会合并到什么位置? ABCABCBABC?
  • @tobias_k,由于 OP 指定了部分重叠,我想说结果应该是 ['ABC', 'B', 'ABC'],即没有部分重叠。

标签: python string merge biopython


【解决方案1】:

这是一个快速排序解决方案:

s = ['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV']
new_s = sorted(s, key=lambda x:s[0].index(x[0]))
a = new_s[0]
b = new_s[-1]
final_s = a[:a.index(b[0])]+b

输出:

'SGALWDVPSPV'

此程序按每个元素的第一个字符的索引值对s 进行排序,以尝试找到将第一个元素与所需输出之间的重叠距离最大化的字符串。

【讨论】:

  • 这假设a)列表中的顺序无关紧要,2)重叠总是1,并且iii)字符串的数量小于字符的数量,对吧?我无法从问题中阅读/解释这些内容。
  • @tobias_k 好点,但是,我认为 OP 需要澄清,特别是关于您在上面发布的其他输入。
  • 列表的顺序无关紧要,字符串可以在任一方向建立。重叠不一定总是 1。字符串可能比字符多。我目前正在尝试实现它并看看它是如何工作的,听起来它可能需要一些调整。
  • 虽然建议的解决方案适用于当前建议的“非常短的肽”序列及其在列表中的顺序和方向,但它不尊重间隙惩罚、氨基酸替换和逆序比对。当前的答案违背了独角兽真实存在的梦想。
【解决方案2】:

我提出的解决方案具有更具挑战性的测试清单:

#strFrag = ['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV']
strFrag = ['ALWDVPS', 'SGALWDV', 'LWDVPSP', 'WDVPSPV', 'GALWDVP', 'LWDVPSP', 'ALWDVPS']

for repeat in range(0, len(strFrag)-1):
    bestMatch = [2, '', ''] #overlap score (minimum value 3), otherStr index, assembled str portion
    for otherStr in strFrag[1:]:
        for x in range(0,len(otherStr)):
            if otherStr[x:] == strFrag[0][:len(otherStr[x:])]:
                if len(otherStr)-x > bestMatch[0]:
                    bestMatch = [len(otherStr)-x, strFrag.index(otherStr), otherStr[:x]+strFrag[0]]
            if otherStr[:-x] == strFrag[0][-len(otherStr[x:]):]:
                if x > bestMatch[0]:
                    bestMatch = [x, strFrag.index(otherStr), strFrag[0]+otherStr[-x:]]
    if bestMatch[0] > 2:
        strFrag[0] = bestMatch[2]
        strFrag = strFrag[:bestMatch[1]]+strFrag[bestMatch[1]+1:]

print(strFrag)       
print(strFrag[0])

基本上,代码会将每个字符串/片段与列表中的第一个进行比较,并找到最佳匹配(大部分重叠)。它逐步合并列表,合并最佳匹配并删除单个字符串。代码假定字符串/片段之间没有无法填充的间隙(否则答案可能不会导致最长的组装。可以通过随机化起始字符串/片段来解决)。还假设不存在反向补码(重叠群组装的假设不佳),这将导致无意义/不匹配的字符串/片段。我已经包含了一种限制最小匹配要求的方法(更改 bestMatch[0] 值)以防止错误匹配。最后一个假设是所有匹配都是精确的。在组装序列时允许灵活地允许错配使问题变得相当复杂。我可以根据要求提供组装不匹配的解决方案。

【讨论】:

    【解决方案3】:

    要确定两个字符串ab的重叠,您可以检查b的任何前缀是否是a的后缀。然后,您可以在一个简单的循环中使用该检查,聚合结果并根据重叠对列表中的下一个字符串进行切片。

    lst = ['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV']
    
    def overlap(a, b):
        return max(i for i in range(len(b)+1) if a.endswith(b[:i]))
    
    res = lst[0]
    for s in lst[1:]:
        o = overlap(res, s)
        res += s[o:]
    print(res) # SGALWDVPSPV
    

    或者使用reduce:

    from functools import reduce # Python 3
    print(reduce(lambda a, b: a + b[overlap(a,b):], lst))
    

    这可能不是超级高效,复杂度约为 O(n k),其中 n 是列表中的字符串数,k 是每个字符串的平均长度。您可以通过仅测试b 的假定重叠的最后一个字符是否是a 的最后一个字符来使其更高效,从而减少生成器表达式中的字符串切片和函数调用量:

    def overlap(a, b):
        return max(i for i in range(len(b)) if b[i-1] == a[-1] and a.endswith(b[:i]))
    

    【讨论】:

    • 附录:这个答案假设字符串按照它们在列表中出现的顺序重叠,但似乎情况并非如此。这使得问题方式变得更加复杂......
    • 我现在已经对初始列表进行了排序,以便我可以确定它们确实按照它们在列表中出现的顺序重叠。我当前面临的问题是,如果有两个相同的字符串相差一个字符,那么我想扔掉一个。所以如果 'WDVPSPV' 和 'WDVPSPT' 都在那里,我想扔掉一个,或者至少删除不同的字符。
    • @AdamPrice 我很好奇:你是如何订购它们的?你知道顺序吗,他们只是不在列表中的那个顺序吗?确定最佳顺序(产生最短的整体字符串)意味着尝试 n!排列,不是吗?
    • 关于“一个字符不同”的问题:您可以找到列表中所有连续字符串对的编辑距离,并检查它是否为1。
    • 这其实是基于一些蛋白质数据。我们有一些粗略的头寸数据,可以让我们以我们认为正确的方式对它们进行排序。我将它们分组到相邻的集合中,然后我可以根据先前显示它们是相邻序列的过滤对它们进行相对排序。所以我们知道它们在同一个邻居中,然后我可以根据我们计算邻居时的相对位置信息对那个小集合进行排序。
    【解决方案4】:

    这是我的解决方案,从 OP 的角度来看,它接近于蛮力。它不受顺序的困扰(随机随机播放以确认),列表中可能存在不匹配的元素,以及其他独立的匹配项。假设重叠意味着不是一个适当的子集,而是独立的字符串,在开始和结束时具有共同的元素:

    from collections import defaultdict
    from random import choice, shuffle
    
    def overlap(a, b):
        """ get the maximum overlap of a & b plus where the overlap starts """
    
        overlaps = []
    
        for i in range(len(b)):
            for j in range(len(a)):
                if a.endswith(b[:i + 1], j):
                    overlaps.append((i, j))
    
        return max(overlaps) if overlaps else (0, -1)
    
    lst = ['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV', 'NONSEQUITUR']
    
    shuffle(lst)  # to verify order doesn't matter
    
    overlaps = defaultdict(list)
    
    while len(lst) > 1:
        overlaps.clear()
    
        for a in lst:
            for b in lst:
                if a == b:
                    continue
    
                amount, start = overlap(a, b)
                overlaps[amount].append((start, a, b))
    
        maximum = max(overlaps)
    
        if maximum == 0:
            break
    
        start, a, b = choice(overlaps[maximum])  # pick one among equals
    
        lst.remove(a)
        lst.remove(b)
        lst.append(a[:start] + b)
    
    print(*lst)
    

    输出

    % python3 test.py
    NONSEQUITUR SGALWDVPSPV
    %
    

    计算所有重叠并将最大的重叠组合成一个元素,替换原来的两个,然后重新开始处理,直到我们只剩下一个元素或没有重叠。

    overlap() 函数效率极低,可能可以改进,但如果这不是匹配 OP 所需的类型,那也没关系。

    【讨论】:

      【解决方案5】:

      一旦肽开始增长到 20 个氨基酸 cdlane's 代码阻塞和垃圾邮件(多个)不正确的答案(多个)具有各种氨基酸长度。

      尝试添加和使用带有或不带有“D”的AA序列“VPSGALWDVPS”,代码开始无法完成任务,因为N-和C-末端增长并且不反映Adam Price的要求。输出是:'SGALWDVPSGALWDVPSPV',因此尽管付出了努力,但 100% 不正确。

      Tbh imo 只有一个 100% 的答案,那就是在 BioPython 包中使用 BLAST 及其 protein search page 或 BLAST。或者调整 cdlane 的代码以反映 AA 的空白、替换和 AA 添加。

      【讨论】:

      • cdlane 提供的代码适用于这个短距离,但超出它会失败。 AA 替换(A->V 或 S>T)和 AA 之间的差距在 cdlane 或其他应考虑的示例(缺乏背景)中不是伪造的。
      • OP 说,“我有一些简单的短字符串,只是想以简单的方式正确合并它们”。我没有声称我的解决方案在生物学上是有效的。它试图解决陈述的问题。如果您和 OP 想要在事后更改问题的参数,那么这是一个 不同 问题,您应该将其作为不同的问题发布。
      • 你对他的问题是正确的,你的方法是一个很好的茶歇预告片。为此,我对其进行了标记和投票,因为它是一个很好的代码游戏。但是从我的专业角度来看,我必须标记它并提供 cmets 以适应问题。当 OP 提出问题时,我相信你做了亚当斯的“功课”。我记得大约 13 年前的此类问题......(景点......我变老了)。所以..你的建议很好,所以我会建议“旗帜”审稿人详细说明和深化这个问题......但是亚当也应该至少有一个他自己的解决方案......?
      • 这里的技术性评论似乎就足够了。
      【解决方案6】:

      挖掘一个旧线程,但今天必须自己解决这个问题。

      对于这种特定情况,其中片段已经有序,并且每个片段重叠相同的数量(在本例中为 1),以下相当简单的串联工作,尽管可能不是世界上最强大的解决方案:

      lst = ['SGALWDV', 'GALWDVP', 'ALWDVPS', 'LWDVPSP', 'WDVPSPV']
      reference = "SGALWDVPSPV"
      string = "".join([i[0] for i in lst] + [lst[-1][1:]])
      reference == string
      True
      

      【讨论】:

        猜你喜欢
        • 2019-03-02
        • 1970-01-01
        • 2021-10-27
        • 1970-01-01
        • 2020-01-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-01
        相关资源
        最近更新 更多