字符串操作似乎效率低下

时间:2013-11-07 12:34:19

标签: python

我认为我的代码效率太低。我猜测它与使用字符串有关,尽管我不确定。这是代码:

genome = FASTAdata[1]
genomeLength = len(genome);

# Hash table holding all the k-mers we will come across
kmers = dict()

# We go through all the possible k-mers by index
for outer in range (0, genomeLength-1):
    for inner in range (outer+2, outer+22):
        substring = genome[outer:inner]
        if substring in kmers:              # if we already have this substring on record, increase its value (count of num of appearances) by 1
            kmers[substring] += 1
        else:
            kmers[substring] = 1            # otherwise record that it's here once

这是为了搜索长度最多为20的所有子串。现在这段代码似乎永远不会终止,所以这里有些错误。在字符串上使用[:]会导致巨大的开销吗?如果是这样,我可以用什么替换它?

为了清楚起见,有问题的文件接近200mb,非常大。

2 个答案:

答案 0 :(得分:1)

我建议使用动态编程算法。问题是,对于所有未找到的inner字符串,您再次使用附加的字符重新搜索它们,因此当然也找不到这些字符串。我没有考虑特定的算法,但这肯定是动态编程的一种情况,你记住你已经搜索过的内容。作为一个非常糟糕的例子,记住所有未找到的长度为1,2,3的substrings,并且永远不会在下一次迭代中扩展这些基数,其中字符串只会更长。

答案 1 :(得分:1)

您应该使用memoryview来避免创建子字符串,因为[:]将返回“视图”而不是副本,但是您必须使用Python 3.3或更高版本(在此之前,它们不是哈希的)。

此外,Counter会简化您的代码。

from collections import Counter

genome = memoryview("abcdefghijkrhirejtvejtijvioecjtiovjitrejabababcd".encode('ascii'))
genomeLength = len(genome)

minlen, maxlen = 2, 22

def fragments():
    for start in range (0, genomeLength-minlen):
        for finish in range (start+minlen, start+maxlen):
            if finish <= genomeLength:
                yield genome[start:finish]

count = Counter(fragments())
for (mv, n) in count.most_common(3):
    print(n, mv.tobytes())

产生

4 b'ab'
3 b'jt'
3 b'ej'

我的笔记本电脑上有一个1,000,000字节的随机数组需要45秒,但2,000,000会导致交换(超过8GB内存使用)。但是,由于您的片段大小很小,您可以轻松地将问题分解为数百万个子序列,然后在最后组合结果(只需注意重叠)。这将为200MB阵列提供约3小时的总运行时间,幸运。

PS要明确的是,通过“在最后组合结果”,我假设您只需要为每个1M子序列保存最流行的,例如,将它们写入文件。你不能将计数器保留在内存中 - 这就是使用8GB的内存。如果你有数千次出现的碎片,这很好,但显然不适用于较小的数字(你可能会在每个200 1M的子序列中看到一个碎片,所以永远不要保存它,例如)。换句话说,结果将是下限 不完整,特别是在较低频率下(仅当在每个子序列中找到并记录片段时,值才完整)。如果您需要确切的结果,这是不合适的。