【问题标题】:In-place dictionary inversion in PythonPython中的就地字典反转
【发布时间】:2011-03-25 23:52:52
【问题描述】:

我需要反转一个列表字典,我不知道如何准确地用英语解释它,所以这里有一些代码可以满足我的需求。只是占用了太多内存。

def invert(oldDict):
    invertedDict = {}
    for key,valuelist in oldDict.iteritems():
        for value in valuelist:
            try:
                entry = invertedDict[value]
                if key not in entry:
                    entry.append(key)
            except KeyError:
                invertedDict[value] = [key]
    return invertedDict

原始是列表的字典,结果是列表的字典。这“反转”了它。

test = {}
test[1] = [1999,2000,2001]
test[2] = [440,441]
test[3] = [440,2000]

print invert(test)

这给出了:

{2000: [1, 3], 2001: [1], 440: [2, 3], 441: [2], 1999: [1]}

我需要知道这是否可以就地完成,因为我当前的策略是使用我正在使用的字典超过我机器上的物理内存量。你能想出一种使用生成器的方法吗?

【问题讨论】:

  • 你试过shelve吗?
  • 我不知道搁置,谢谢。我猜新旧词典都不需要满载就可以操作吧?
  • shelve 仅适用于字符串键。不过你可以解决它

标签: python list hashtable generator


【解决方案1】:

这并没有就地执行,而是通过使用 popitem() 消耗 oldDict

from collections import defaultdict
def invert(oldDict):
    invertedDict = defaultdict(list)
    while oldDict:
        key, valuelist = oldDict.popitem()
        for value in valuelist:
            invertedDict[value].append(key)
    return invertedDict

我有一种感觉,除非大小增加,否则 dict 永远不会调整大小,因此您可能需要定期添加+删除一个虚拟项目。见Shrinkage rate

from collections import defaultdict
def invert(oldDict):
    invertedDict = defaultdict(list)
    i=0
    while oldDict:
        key, valuelist = oldDict.popitem()
        for value in valuelist:
            invertedDict[value].append(key)
        i+=1
        if i%1000==0: # allow the dict to release memory from time to time
            oldDict[None]=None
            del oldDict[None]
    return invertedDict

【讨论】:

  • 是的,这正是我想建议的。从旧字典中删除对象,这样你应该保持内存使用相当稳定(至少在垃圾收集发生时)。
  • 这是强制 dict 调整大小的聪明方法。
  • 我现在正在运行这个方法,到目前为止它看起来不错。
  • 感谢您注意到“if key not in entry:”是不必要的,这是一个加号。
【解决方案2】:

我实际上看不出有任何方法可以显着改善您当前算法的内存使用情况。您确实使用迭代器而不是直接创建新列表/字典,因此唯一重要的内存使用来自原始字典和新的倒排字典。

如果您没有足够的 RAM 来使用您实际使用的字典运行此算法,我所能想到的就是以某种方式避免同时将原始字典和反转字典保存在内存中。一种方法是在将项目添加到倒置的 dict 时从原始 dict 中删除它们,可以这样完成:

def invert(old_dict):
    inverted = collections.defaultdict(list)
    while old_dict:
        k,v = old_dict.popitem()
        for vi in v:
            inverted[vi].append(k)
    return inverted

(请注意,我还使用了defaultdict 来简化代码,但如果你真的需要一个纯粹的dict,而不是一个子类,你可以像原来使用try/except 那样做)

如果您想在算法完成后保持原始字典和倒排字典都可用,我能想到的就是将它们存储在磁盘文件中,并找到某种方法一次只加载一个片段。我不知道有任何标准 Python 模块能够将 dict 存储到磁盘并一次只加载其中的一部分,因此您可能必须为此编写自己的代码。

【讨论】:

    【解决方案3】:

    我没有直接的答案。这是我的一些想法。

    1. 我觉得你想做的可以叫Inverted index

    2. 我不相信它可以就地完成,我也不认为这是正确的策略。您应该查看基于磁盘的解决方案。也许对您的原始数据结构进行排序或组织,将其写入一个或多个文件,然后将其读回并将它们合并到您的最终数据结构中。

    【讨论】:

      【解决方案4】:

      如果算法正确,现代机器上的 RAM 可能需要数百万个条目才能用完。假设这一点,您必须使用一些持久性存储来一次只处理数据块。为什么不使用具有 2 列的简单数据库表来存储字典?

      key  value
      1    1999
      1    2000
      1    2001
      2    440
      2    441
      ...
      

      然后,您可以通过在所需列上选择 order by 并使用简单的 python 代码对其他列中的值进行分组来将任一列用作键。

      【讨论】:

      • 我想我以后会用 shelve,但现在,gnibbler 的把戏确实奏效了。
      猜你喜欢
      • 1970-01-01
      • 2019-11-02
      • 2011-06-14
      • 1970-01-01
      • 2021-08-21
      • 2013-01-11
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多