按键结合两个大字典 - 最快的方法

时间:2015-03-18 16:10:52

标签: python dictionary python-2.x python-performance

我有两个大字典:这是一个示例,但你可以想象每个字典有近100k的记录。

d1 = {'0001': [('skiing',0.789),('snow',0.65),('winter',0.56)],'0002': [('drama', 0.89),('comedy', 0.678),('action',-0.42), ('winter',-0.12),('kids',0.12)]}

d2 = {'0001': [('action', 0.89),('funny', 0.58),('sports',0.12)],'0002': [('dark', 0.89),('Mystery', 0.678),('crime',0.12), ('adult',-0.423)]}

截至最终目标,我希望有一个字典,它按每个字典的键组合值:

{'0001': [('skiing', 0.789), ('snow', 0.65), ('winter', 0.56), [('action', 0.89), ('funny', 0.58), ('sports', 0.12)]], '0002': [('drama', 0.89), ('comedy', 0.678), ('action', -0.42), ('winter', -0.12), ('kids', 0.12), [('dark', 0.89), ('Mystery', 0.678), ('crime', 0.12), ('adult', -0.423)]]}

我实现这一目标的方式是:

for key,value in d1.iteritems():
     if key in d2:
             d1[key].append(d2[key])

但是在很多地方读完后我发现iteritems()非常慢,实际上并没有使用C数据结构来实现它,而是使用python函数。如何快速有效地完成组合/合并过程。

5 个答案:

答案 0 :(得分:2)

我认为您需要合并dicts

from collections import Counter
res = Counter(d1) + Counter(d2)
>>>res
Counter({'0001': [('skiing', 0.789), ('snow', 0.65), ('winter', 0.56 **...**

例如

from collections import Counter

d1 = {"a":[1,2], "b":[]}
d2 = {"a":[1,3], "b":[5,6]}

res = Counter(d1)+Counter(d2)

>>>res
Counter({'b': [5, 6], 'a': [1, 2, 1, 3]})

即使这种方法也支持dicts中不等数量的密钥,例如

d1 = {"a":[1,2], "b":[]}
d2 = {"a":[1,3], "b":[5,6], "c":["ff"]}

>>>res
Counter({'c': ['ff'], 'b': [5, 6], 'a': [1, 2, 1, 3]})

答案 1 :(得分:2)

for k, v in d2.items():
    if k in d1:
        d1[k].extend(v)
    else:
        d1[k] = v  

答案 2 :(得分:1)

d1 = {'0001': [('skiing',0.789),('snow',0.65),('winter',0.56)],'0002': [('drama', 0.89),('comedy', 0.678),('action',-0.42), ('winter',-0.12),('kids',0.12)]}
d2 = {'0001': [('action', 0.89),('funny', 0.58),('sports',0.12)],'0002': [('dark', 0.89),('Mystery', 0.678),('crime',0.12), ('adult',-0.423)]}

for x in set(d1).intersection(set(d2)):
    d1[x].extend(d2[x])

也许你可以尝试这个程序。起初我得到了d1和d2之间的交集,因此我可以为此任务执行最少的迭代次数。

答案 3 :(得分:1)

你可以通过 -

来做到这一点
>>> d1 = {'0001': [('skiing',0.789),('snow',0.65),('winter',0.56)],'0002': [('drama', 0.89),('comedy', 0.678),('action',-0.42), ('winter',-0.12),('kids',0.12)]}
>>> d2 = {'0001': [('action', 0.89),('funny', 0.58),('sports',0.12)],'0002': [('dark', 0.89),('Mystery', 0.678),('crime',0.12), ('adult',-0.423)]}
>>> dict( (n, d1.get(n, []) + d2.get(n, [])) for n in set(d1)|set(d2) )
{'0001': [('skiing', 0.789), ('snow', 0.65), ('winter', 0.56), ('action', 0.89), ('funny', 0.58), ('sports', 0.12)], '0002': [('drama', 0.89), ('comedy', 0.678), ('action', -0.42), ('winter', -0.12), ('kids', 0.12), ('dark', 0.89), ('Mystery', 0.678), ('crime', 0.12), ('adult', -0.423)]}

答案 4 :(得分:1)

如果您的输入必须是双语,我认为您不会发现比iteritems更快的内容。如果一个dict的键比另一个更少,你应该迭代较小的一个来减少迭代。

任何涉及将dict转换为不同数据类型的解决方案在创建时间上的成本都会比通过高效循环节省的成本更高。而不是迭代一次,你必须迭代三次(两次创建两个新集合,一次运行你的合并)。

如果您在最初创建集合时可以选择使用不同的数据类型,则迭代列表(使用索引而不是键)会稍微快一些。当然,这意味着你失去了dicts可能为你提供的所有其他优势。

timeit给出了以下各种建议方法的速度,给出了200个密钥的两个序列(两个dicts的密钥相同):

为了给设置交叉点提供另一个机会,只允许d2的一半键实际匹配d1的键:

  • iteritem:15.5433290005
  • set intersection:22.1796240807

正如我们所看到的,创建两个集合的成本仍然超过任何潜在的好处(至少在Python 2中,dict.keys()给出了一个列表,而不是一个与集合操作兼容的视图)。

附注:附加vs扩展

在您当前的代码示例中

for key,value in d1.iteritems():
    if key in d2:
        d1[key].append(d2[key])

您要将d2的整个列表作为d1中列表的单个新项目附加,而不是合并列表([1,2].append([3,4]) == [1,2,[3,4]],而不是[1,2,3,4])。您可以从d2循环遍历列表,并多次调用追加,但extend()会更快。