给定一个包含三层键的字典,对值进行求和的最快方法是什么?这是我目前的做法:
from collections import defaultdict
dicts = [ {'a':{'b':{'c':1}}}, {'a':{'b':{'c':4, 'e':3}}} ]
def sum_three_deep_dict_values(dicts):
'''Read in two dicts and return a dictionary that contains their outer-joined keys and value sums'''
combined = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
for d in dicts:
for w1, val_dict in d.iteritems():
for w2 in val_dict.iterkeys():
for w3 in val_dict[w2].iterkeys():
combined[w1][w2][w3] += d[w1][w2][w3]
return combined
print sum_three_deep_dict_values(dicts)
此处预期输出为{'a': {'b': {'c': 5, 'e': 3}}}
目标是对两个字典具有相同键的值(例如此处为d[a][b][c]
)求和,并包括来自任一字典的其余键值对输出字典。
有很多关于SO的问题似乎回答了这个问题:“如何总结嵌套词典的价值”?然而,昨晚读完它们,我发现的每一个都涉及一些奇怪的特殊情况或参数,例如“组合/忽略第n层键”,或“在特殊地方应用if条件”。因此,我想提出一个简单的问题:在Python中对双嵌套字典的值进行求和的最佳方法是什么?
答案 0 :(得分:3)
我认为你目前的做法一般都是好的做法。我的建议是尽可能多地删除字典查找。迭代键和值一起应该像只迭代键一样快,所以你也可以将它们组合起来。如果你这样做,最后调用d[w1][w2][w3]
是不必要的,临时密钥查找也是如此。所以像这样:
def sum_three_deep_dict_values(dicts):
'''Read in two dicts and return a dictionary that contains
their outer-joined keys and value sums'''
combined = defaultdict(lambda: defaultdict(lambda: defaultdict(int)))
for layer0 in dicts:
for k1, layer1 in layer0.iteritems():
for k2, layer2 in layer1.iteritems():
for k3, count in layer2.iteritems():
combined[k1][k2][k3] += count
return combined
我冒昧地改变了你的名字计划。
如果您在测试上述内容后仍然担心速度,则可能需要查看其他数据结构或第三方库。但在你这样做之前,试试PyPy - 我发现它经常在vanilla for
循环上提供至少4倍的加速。
另外,请针对原始代码进行测试。我认为我上面的推理是成立的,但它仍然有点推测。我对别人很好奇'建议也是。在你工作的规模上,这可能是一个挑战! (出于好奇,这会带你到现在的代码多长时间?)
更新:我测试了这个并且确实更快,虽然只是一头发:
>>> %timeit sum_three_deep_original(dicts)
1000 loops, best of 3: 1.38 ms per loop
>>> %timeit sum_three_deep_edited(dicts)
1000 loops, best of 3: 1.26 ms per loop
我猜你需要更快的速度来申请。我用PyPy尝试过,我也使用cython编译它(但没有任何修改或类型注释)。 PyPy以66%的加速获胜。普通的python(这次参数略有不同):
:~ $ python -c 'from tdsum import test; test()'
1.63905096054
用cython编译:
:~ $ python -c 'from tdsum import test; test()'
1.224848032
使用PyPy:
:~ $ pypy -c 'from tdsum import test; test()'
0.427165031433
我希望使用定制数据结构的真正的cython版本能够显着超越PyPy。问题是你不能使用dict
并且仍然可以获得你想要的迭代加速,因为cython必须克服Python对象的开销。所以你必须实现自己的哈希表!
我经常想知道为什么cython没有为这个问题提供解决方案;也许那里有numpy
类型可用。我一直在寻找!
答案 1 :(得分:0)
这是一个使用展平函数和膨胀函数的解决方案,用于任意深度嵌套的问题。适合您输入,但没有进行更多测试:
from collections import Counter
def flatten(d, parent=None):
for k, v in d.items():
keys = (k,) if parent is None else parent + (k,)
if isinstance(v, dict):
yield from flatten(v, keys)
else:
yield keys, v
def puffup(c):
top = {}
for k, v in c.items():
current = top # reset walk
for ki in k[:-1]:
if ki not in current:
current[ki] = {}
current[k[-1]] = v
return top
dicts = [ {'a':{'b':{'c':1}}}, {'a':{'b':{'c':4, 'e':3}}} ]
c = Counter()
for d in dicts:
c += dict(flatten(d))
print(puffup(c))
# {'a': {'b': {'c': 5, 'e': 3}}}
我刚看到你正在寻找最快的。虽然更加灵活,但这比上面的答案慢了2.5倍,而且根本没有对输入进行任何跳汰。