【问题标题】:Python Buffer for large string - better or worse?大字符串的 Python 缓冲区 - 好还是坏?
【发布时间】:2015-03-04 21:55:00
【问题描述】:

我正在使用 python 获取一个非常大的字符串(DNA 序列)并尝试从中创建一个后缀树。我的程序在制作嵌套对象很长时间后出现内存错误,所以我认为为了提高性能,从字符串创建缓冲区而不是实际切片字符串可能很有用。两个版本都在下面,并描述了它们的相关问题。

第一个(非缓冲)版本 - 在处理大字符串大约一分钟后,currNode.out[substring[0]] = self 发生 MemoryError。节点(pos, substring[1:])

class SuffixTree(object):

class Node(object):
    def __init__(self, position, suffix):
        self.position = position
        self.suffix = suffix
        self.out = {}

def __init__(self, text):

    self.text = text
    self.max_repeat = 2
    self.repeats = {}
    self.root = self.Node(None, '')
    L = len(self.text)

    for i in xrange(L):

        substring = self.text[-1*(i+1):] + "$"
        currNode = self.root
        self.branch(currNode, substring, L-i-1, 0)

    max_repeat = max(self.repeats.iterkeys(), key=len)
    print "Max repeat is", len(max_repeat), ":", max_repeat, "at locations:", self.repeats[max_repeat]

def branch(self, currNode, substring, pos, repeat):

    if currNode.suffix != '':
        currNode.out[currNode.suffix[0]] = self.Node(currNode.position, currNode.suffix[1:])
        currNode.suffix = ''
        currNode.position = None

    if substring[0] not in currNode.out:

        currNode.out[substring[0]] = self.Node(pos, substring[1:])

        if repeat >= self.max_repeat:
            for node in currNode.out:
                self.repeats.setdefault(self.text[pos:pos+repeat], []).append(currNode.out[node].position)
            self.max_repeat = repeat

    else:

        newNode = currNode.out[substring[0]]
        self.branch(newNode, substring[1:], pos, repeat+1)

**第二版 ** - 考虑到大字符串切片的不断保存可能是问题所在,我使用字符串缓冲区来实现所有切片。然而,这个版本几乎立即给出了一个 MemoryError for substring = buffer(self.text, i-1) + "$"

class SuffixTree(object):

    class Node(object):
        def __init__(self, position, suffix):
            self.position = position
            self.suffix = suffix
            self.out = {}

    def __init__(self, text):

        self.text = text
        self.max_repeat = 2
        self.repeats = {}
        self.root = self.Node(None, '')
        L = len(self.text)

        for i in xrange(L,0,-1):

            substring = buffer(self.text, i-1) + "$"
            #print substring
            currNode = self.root
            self.branch(currNode, substring, i-1, 0)

        max_repeat = max(self.repeats.iterkeys(), key=len)
        print "Max repeat is", len(max_repeat), ":", max_repeat, "at locations:", self.repeats[max_repeat]
        #print self.repeats

    def branch(self, currNode, substring, pos, repeat):

        if currNode.suffix != '':
            currNode.out[currNode.suffix[0]] = self.Node(currNode.position, buffer(currNode.suffix,1))
            currNode.suffix = ''
            currNode.position = None
        if substring[0] not in currNode.out:

            currNode.out[substring[0]] = self.Node(pos, buffer(substring,1))

            if repeat >= self.max_repeat:
                for node in currNode.out:
                    self.repeats.setdefault(buffer(self.text,pos,repeat), []).append(currNode.out[node].position)
                self.max_repeat = repeat

        else:

            newNode = currNode.out[substring[0]]
            self.branch(newNode, buffer(substring,1), pos, repeat+1)

我对缓冲区的理解在某种程度上是错误的吗?我认为使用它们可以帮助解决我的程序遇到的内存问题,而不是让它变得更糟。

【问题讨论】:

  • 您使用什么样的序列文件,它们的大小是多少?还有你的 RAM 是什么样子的?
  • 我正在输入 FASTA 文件,这些文件没有压缩或任何东西,我在 16gb 内存的笔记本电脑上运行。
  • FASTA 文件的大小是多少?或者如果有多个,有多少个,它们的平均大小是多少?
  • 它像 FASTA 文件一样小,只有 4.5MB。我认为也许我编写程序的方式是问题所在。从本质上讲,它是在非常深的层次上使对象在对象内部的对象内部。我相信它可能在某个时候内存不足,无法再创建另一个新的“节点”对象并给出内存错误。我不确定如何正确调试内存问题,所以这只是假设。

标签: python string optimization buffer


【解决方案1】:

处理序列数据可能会占用大量内存,尤其是在处理数十 GB 或更大范围内的 fastq 文件时。在某个时候,您只需要使用具有更多 RAM 的机器。因为您没有提供文件大小信息或有关您的计算资源的信息,所以无法判断是否是这种情况。请在有机会时提供该信息。

无论如何,您似乎应该使用一些 Bio python 工具,这将使处理序列数据的效率更高。这是因为 Bio python 的大部分使用 C 数据结构。

首先,您应该将序列表示为Seqs,而不是字符串。

from Bio.Seq import Seq

sequence = Seq('ACTG')

其次,后缀树是trie 的完美工作。

Trie - Wikipedia

from Bio import trie

self.out = trie.trie()
# You can work with this object just as you would with a dictionary.
# self.out[key] = value

使用这些 biopython 数据结构应该可以提高内存效率,并且通常是在 python 中处理序列数据时的一个好习惯。

另外,我不确定您的最终目标是什么,但您可能想研究更有效的后缀数组。许多从头组装器(例如 ABySS)使用它进行组装。

【讨论】:

  • 我的目标不是专业导向而是学习导向,所以我试图从我自己的技能组合中创建一个实现,而不是使用 Bio python 附带的。我更像是一个科学人,而不是一个程序人,所以我试图将我的“论文”知识放在后缀树上,以便在 Python 中使用作为练习。不过,我很乐意听到有关我的后缀树实现的任何建议。
  • 我明白了。我有相同的背景(首先是生物学,然后是计算机科学)。然而我很困惑,因为我的建议只是两种替代数据结构,所以本质上你仍然需要开发和实现算法本身,同时以更有效的方式存储你的数据。我很想知道用 self.out = trie.trie() 替换你的 self.out = {} 是如何工作的,就好像我理解代码一样,这是你保存嵌套对象的地方,而 trie 没有为您创建后缀,但其目的与您的字典相同,但内存效率更高。
【解决方案2】:

将“$”添加到缓冲区中会杀死它。这会导致它强制自己变成一个字符串,这会导致所有与以前相同的复制。如果确实需要追加字符,可以尝试使用缓冲区列表,而不是实际将字符追加到单个缓冲区。

【讨论】:

    猜你喜欢
    • 2010-10-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-24
    相关资源
    最近更新 更多