提高列表中子字符串搜索的速度

时间:2013-11-08 19:38:46

标签: python

我对Python很陌生,我正在努力提高一段代码的速度。

我有一个包含500k DNA序列的字典。作为关键,我有序列的标识符,而作为值,我有相应的DNA序列。这些序列长度可变(它只是一个包含CTACTA的字符串......),可能有200到60k个核苷酸。我需要去除作为较大序列的子串的DNA序列。

我写了这个:

def remove_subs():

    #Create a list of values based on reversed lenght
    LISTA=sorted(list(x for x in finaldic.values()), key=len, reverse=True)

    LISTA2=[]

    for a in range(len(LISTA)):
        #run the same list but in opposite direction 
        for b in range(len(sorted(LISTA,key=len))):
            if len(LISTA[b])<len(LISTA[a]):
                if LISTA[a].find(LISTA[b])!=-1 or Bio.Seq.reverse_complement(LISTA[a]).find(LISTA[b])!=-1 and LISTA[b]!=LISTA[a]:
                    LISTA2.append(LISTA[a])

我试图通过运行两个for循环来识别这些子串序列,一个列表只包含DNA序列(按长度排序),使用内置的相反方向.find

此代码运行良好,但需要很长时间才能运行这么多信息。我很确定存在一些更快的选择。

你能帮忙吗?

5 个答案:

答案 0 :(得分:1)

从算法的角度来看,您可能应该看一下suffix trees。首先,您要从要查找的字符串构建一个通用后缀树,其具有构造的O(n)时间复杂度(其中n =要搜索的所有字符串中的字符数)。然后,您可以查询该树,如果其中包含子字符串,则该字符串具有O(m)时间复杂度,其中m是子字符串的长度。从本质上讲,这是尽可能快的。


描述一些后缀树库的堆栈溢出问题:

python: library for generalized suffix trees

不幸的是,这里的示例不是非常成熟的代码库......有些C库更加注重优化等等。尽管如此,像suffix tree algorithm这样的东西应该是代码的简单替代品:

import SubstringDict
d = SubstringDict.SubstringDict()
d['foobar'] = 1  
d['barfoo'] = 2
d['forget'] = 3
d['arfbag'] = 4

print(d['a'])
# [1, 2, 4]
print(d['arf'])
# [2, 4]
print (d['oo'])
# [1, 2]
print(d['food'])
# []

在生物信息学中搜索和匹配字符串是一个非常大且活跃的领域,关于这个问题有很多文献。

答案 1 :(得分:0)

只是为了清理它,所以它更容易理解:

def remove_subs():
    list_a = sorted(list(x for x in finaldic.values()), key=len, reverse=True)
    matches = []
    for first in list_a:
        for second in (sorted(list_a, key=len)):
            if first in second or first in Bio.Seq.reverse_complement(second):
                matches.append(first)
                break

您只需使用break即可看到加速。

使用以下方法可以缩小:

def remove_subs():
    list_a = sorted(list(x for x in finaldic.values()), key=len, reverse=True)
    matches = []
    for s in list_a:
        if any(substring in s for substring in list_a):
            matches.append(s)

另外,使用this topic作为算法的参考。

答案 2 :(得分:0)

以下是一些可能会提高您速度的修复方法。至少它会使你的代码更加惯用于python。

def remove_subs():

    #Create a list of values based on reversed lenght
    list_a=sorted((x for x in finaldic.values()), key=len, reverse=True)

    list_a_2=[]

    for a in list_a:
        #run the same list but in opposite direction 
        for b in sorted(list_a,key=len):
            if len(b)<len(a):
                if b in a or b in Bio.Seq.reverse_complement(a) and b!=a:
                    list_a_2.append(a)

两个主要变化:1)我没有使用.find方法,而是使用python的in运算符进行搜索。 2)不是索引列表,而是直接循环它们。

你可能可以逃脱if len(b) < len(a)条件,因为b永远不会在a中,如果不是这样的话。

答案 3 :(得分:0)

我有一个想法,可以帮助,如何散列序列?如果最小序列的长度是200,那么我会做一个窗口大小为200的滚动哈希(http://en.wikipedia.org/wiki/Rolling_hash)。然后我会使用哈希作为字典的键,它将保存一个序列列表身份标识。然后,如果有一个大小列表&gt; 1,它是子串的候选者(可能存在碰撞),你可以使用find。

答案 4 :(得分:0)

没有任何测试数据或自包含代码,很难测试,但我会指出在循环内排序很少是个好主意。这应该使运行时间从O(n ^ 3 * logn)下降到O(n ^ 2):

def remove_subs():
    list_a_backward = sorted(list(x for x in finaldic.values()), key=len, reverse=True)
    list_a_forward = list_a_backward
    list_a_forward.reverse()

    matches = []
    for first in list_a_backward:
        for second in list_a_forward:
            if first in second or first in Bio.Seq.reverse_complement(second):
                matches.append(first)
                break

你也可以尝试Pypy,因为你似乎正在运行纯python。如果不这样做,numba或Cython可能会有所帮助。