Python中的就地字典反转

时间:2010-08-05 19:00:34

标签: python list hashtable generator

我需要反转一个列表字典,我不知道如何用英语解释它,所以这里有一些代码可以做我想要的。它只需要太多的记忆。

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]}

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

4 个答案:

答案 0 :(得分:5)

这不适用,但使用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

答案 1 :(得分:2)

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

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

然后,您可以使用任意列作为键,方法是在所需列上选择order by,并使用简单的python代码对其他列中的值进行分组。

答案 2 :(得分:1)

我实际上没有看到任何方式可以显着提高当前算法的内存使用率。您确实使用迭代器而不是直接创建新的列表/ dicts,因此唯一重要的内存使用来自原始字典和新的反向字典。

如果你没有足够的RAM来运行这个算法和你实际使用的字典,我所能想到的就是以某种方式避免同时将原始字典和倒置字典保留在内存中。一种方法是将原始字典中的项目添加到倒置字典中,这可以这样做:

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而不是一个子类,那么你可以做一些像{{1 }} / try

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

答案 3 :(得分:0)

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

  1. 我认为您想要做的事情可以称为Inverted index

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