我对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
此代码运行良好,但需要很长时间才能运行这么多信息。我很确定存在一些更快的选择。
你能帮忙吗?
答案 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可能会有所帮助。