Python 在生成dict时高效地处理大量字符串

Python 在生成dict时高效地处理大量字符串,python,python-3.x,Python,Python 3.x,我对Python相当陌生,尤其是对处理大量数据非常陌生。我正在做一个有趣的小项目,它实际上是我以前用另一种语言做过的事情的高级版本 现在,我正在加载一个相当大的(100mb+)文本文档,将其分解为单词,然后确定每个前缀后面的单词频率(每个前缀是一个或多个单词)。在Python中实现非常简单和有趣,我最终得到了以下内容: def prefix(symbols): relationships = {} for i in reversed(range(len(symbols))):

我对Python相当陌生,尤其是对处理大量数据非常陌生。我正在做一个有趣的小项目,它实际上是我以前用另一种语言做过的事情的高级版本

现在,我正在加载一个相当大的(100mb+)文本文档,将其分解为单词,然后确定每个前缀后面的单词频率(每个前缀是一个或多个单词)。在Python中实现非常简单和有趣,我最终得到了以下内容:

def prefix(symbols):
    relationships = {}

    for i in reversed(range(len(symbols))):
        prefix = seperator.join(symbols[i:i+samples])
        suffix = None

        if i+samples < len(symbols):
            suffix = symbols[i+samples]

        if prefix not in relations:
            relations[prefix] = {}

        if suffix not in relations[prefix]:
            relations[prefix][suffix] = 1
        else:
            relations[prefix][suffix] += 1

    return relations
def前缀(符号):
关系={}
对于反向(范围(len(符号)))中的i:
前缀=分隔符.join(符号[i:i+samples])
后缀=无
如果i+样本
(函数名、参数和全局“samples”的使用在我解决问题时只是暂时的)

这很有效,大约需要20秒来处理来自维基百科的60mb顶级文章的纯文本转储。但是,将样本大小(“samples”变量)从1增加到甚至4或5,会大大提高内存使用率(正如预期的那样——大约有1000万个字,每个“samples”中有许多字被切分并合并成一个新字符串)。这将很快接近并达到2GB的内存限制

我应用的一种缓解此问题的方法是在迭代时从内存中删除初始单词,因为它们不再需要(单词列表可以简单地构造为函数的一部分,因此我不会修改传入的任何内容)

def前缀(符号):
关系={}
n=长度(符号)
对于范围(n)中的i:
前缀=分隔符.join(符号[0:示例])
后缀=无
如果样本
这确实有帮助,但没有多大帮助,并且以牺牲一些性能为代价

所以我想问的是,这种方法是否相当有效或被推荐,如果不是,什么更合适?我可能缺少一些避免重复创建字符串和复制列表的方法,因为这些对我来说都是新的。 我正在考虑:

  • 对符号/单词列表进行分块、处理,将关系转储到磁盘,并在事件发生后将其组合
  • 使用Redis之类的工具,而不是将关系始终保持在Python中

为任何建议或帮助干杯

在Python中处理大型字符串时,如果需要进行大量更改,主要建议如下:

  • 将字符串转换为列表
  • 使用列表完成所有工作
  • 完成后,转换回字符串

  • 原因是Python中的字符串是不可变的。例如,作为
    符号[i:i+samples]
    的每个操作都强制Python分配新内存,复制所需字符串,并将其作为新字符串对象返回。因此,当您需要对字符串进行许多更改(按索引更改、拆分)时,您最好使用可变的列表。

    Re。为了加快进程,我发现使用try/except块来更新哈希表很有用。例如:

    try:
        relationships[prefix][suffix] += 1
    except KeyError:
        relationships[prefix][suffix] = 1
    

    上述代码没有使用“in”来检查密钥,然后更新/创建需要对该密钥进行另一次检查的密钥,而是消除了一次检查,因此工作更快。

    使用,
    迭代器/u符号=iter(列表/u符号)
    ,并对该iter执行
    下一步()
    ,即
    迭代器/u符号。下一步(

    阅读更多


    虽然它是用Java解释的,但我认为概念是相同的。

    这与您关于内存效率/性能的问题无关,而是与您的
    关系[前缀]
    关系[前缀][后缀]
    字典您可能希望分别使用a和a-它们应该是完美的组合,使您的代码更加简洁。如果
    符号是
    列表
    ,不要使用
    del符号[0]
    ,它需要移动列表中的每个元素,a
    集合。deque()
    应该更有效。理想情况下,您可以制作
    符号
    生成器,在使用输入文件时生成
    样本
    单词。此处哪个变量的内存在增加?此处
    搁置
    是否有帮助?而@LukasGraf我尝试了这两种方法,事实上这也是我转向Python3.x(性能更好的计数器)的部分原因,但毕竟我发现“手动”操作可以获得更好的性能。不过,之后我可能会回到使用带有计数器的defaultdict!您肯定有一个非常好的观点,在Python中,意外地在内存中复制字符串可能会很快发生,应该避免。然而,我认为在OP的例子中,
    symbols
    是一个(单词)列表。是的,符号是一个字符串列表——我相当肯定这意味着我从它们那里得到的片段至少不是副本。内存使用问题的出现是因为我立即将它们组合成一个(新的?)字符串
    prefix=separator.join(符号[0:samples])
    (只是注意到了分隔符的输入错误,哈!)。但是,我不知道如何才能避免这种情况,因为我相信我需要能够立即添加关系(通过dict)。我以前也使用过元组作为键,但这并没有提高内存使用率——我相当肯定它也会生成副本。
    try:
        relationships[prefix][suffix] += 1
    except KeyError:
        relationships[prefix][suffix] = 1