Python:求和三层字典的值

时间:2015-05-19 12:15:09

标签: python dictionary

给定一个包含三层键的字典,对值进行求和的最快方法是什么?这是我目前的做法:

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中对双嵌套字典的值进行求和的最佳方法是什么?

2 个答案:

答案 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倍,而且根本没有对输入进行任何跳汰。