如何对字典元素求和

42

在Python中,我有一个字典列表:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

我想要一个最终的字典,其中包含所有字典的总和。 即结果将为:{'a':5, 'b':7}

注:列表中的每个字典都包含相同数量的键值对。

11个回答

60
你可以使用 collections.Counter
counter = collections.Counter()
for d in dict1: 
    counter.update(d)

或者,如果你更喜欢一行代码:

functools.reduce(operator.add, map(collections.Counter, dict1))

1
sum(map(collections.Counter, dict1),Counter())。但我不确定函数式版本创建所有这些Counters()的相对性能。 - John La Rooy
7
这个答案展示了Python编程的黄金法则:如果Python自带了,就不要重新发明轮子。需要指出的一点是,最终结果counterdict的一个子类实例,如果提问者想要普通的dict,可以添加最后一行代码counter = dict(counter) - Duncan
如果所有字典的键不完全相同,则第一种解决方案将仅输出所有字典共享的键的结果,而第二个一行解决方案将输出所有键的结果(将缺失的键视为值0)。 - roob

18
一个有点难看但只有一行的代码:
dictf = reduce(lambda x, y: dict((k, v + y[k]) for k, v in x.iteritems()), dict1)

我有一个对象列表,而这个字典是一个对象属性,有什么解决方案吗?:( - Nazmul Hasan
1
[ob1, ob2, ob3].. 每个对象都有一个名为 data 的属性,它返回一个字典 {'a':2, 'b':3},类似于这样。 - Nazmul Hasan
1
dictf = reduce(lambda x, y: dict((k, v + y.data[k]) for k, v in x.data.iteritems()), dict1 - carl
我的代码使用这个解决方案正常运行。好的,它使用了reduce函数(lambda)。通过对x中的本地支出字典进行迭代,在y的本地支出字典中查找相同键的值并将它们相加,如果值是字典,则添加0。最后生成新的字典并返回它。 - Nazmul Hasan
1
@nazmul hasan:你在6个月后还能理解这个吗?你已经写了3次get_local_expenses(),这是必要的吗?GLC是什么?你读过@paxdiablo的答案吗? - John Machin
显示剩余2条评论

13

如果要添加多个字典,利用sum()应该可以获得更好的性能。

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
>>> from operator import itemgetter
>>> {k:sum(map(itemgetter(k), dict1)) for k in dict1[0]}        # Python2.7+
{'a': 5, 'b': 7}
>>> dict((k,sum(map(itemgetter(k), dict1))) for k in dict1[0])  # Python2.6
{'a': 5, 'b': 7}

添加 Stephan 的建议

>>> {k: sum(d[k] for d in dict1) for k in dict1[0]}            # Python2.7+
{'a': 5, 'b': 7}
>>> dict((k, sum(d[k] for d in dict1)) for k in dict1[0])      # Python2.6
{'a': 5, 'b': 7}

我觉得Stephan的Python2.7代码版本写得非常流畅


2
你使用 mapitemgetter 而不是在内部循环中使用列表推导式(即 dict((k, sum(d[k] for d in dict1)) for k in dict1[0]))的原因是什么? - stephan
@stephan,它以前速度更快...现在似乎速度差不多了。我会将其添加到我的答案中。 - John La Rooy
这个版本的一个不错的补充是,它还检查字典类型,以确保我们可以在其上执行数学运算:{k: sum(d[k] if type(d[k]) in (int, float) else 0 for d in dict1) for k in dict1[0]} - ygbr

10

这可能有所帮助:

def sum_dict(d1, d2):
    for key, value in d1.items():
        d1[key] = value + d2.get(key, 0)
    return d1

>>> dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
>>> reduce(sum_dict, dict1)
{'a': 5, 'b': 7}

5

您也可以使用pandas的sum函数计算总和:

import pandas as pd
# create a DataFrame
df = pd.DataFrame(dict1)
# compute the sum and convert to dict.
dict(df.sum())

这将导致:
{'a': 5, 'b': 7}

它也适用于浮点数:

dict2 = [{'a':2, 'b':3.3},{'a':3, 'b':4.5}]
dict(pd.DataFrame(dict2).sum())

给出正确的结果:
{'a': 5.0, 'b': 7.8}

5
以下代码展示了一种实现方法:
dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

final = {}
for k in dict1[0].keys():           # Init all elements to zero.
    final[k] = 0
for d in dict1:
    for k in d.keys():
        final[k] = final[k] + d[k]  # Update the element.

print final

这将输出:
{'a': 5, 'b': 7}

如你所愿。

或者,受kriss启发,更好但仍易读:

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]

final = {}
for d in dict1:
    for k in d.keys():
        final[k] = final.get(k,0) + d[k]

print final

我渴望那些原始、可读的Python的日子 :-)



你可以将第一个 for 循环简化为 final={}.fromkeys(dict1[0],0)。或者这就是 "可读性" 所指的吗? :) - John La Rooy
这是个好建议,@kriss,而且还很易读,但我认为你的意思是用final.get(k,0) + d[k]替换final[k] + d[k]——如果键不存在,我需要从final字典中获取默认值——我知道它对于d是存在的。 - paxdiablo
@paxdiablo:哎呀!是的,你绝对正确,我颠倒了字典。 - kriss
@paxdiablo:你甚至可以再更进一步(但我对于可读性有所担忧,你可以自己看一下)。使用final = dict((k, v + final.get(k, 0)) for k, v in d.iteritems())代替内部循环。 - kriss
现在它只是开始看起来像“Είναι ένα, ένα χιλιόμετρο μακρινός από εδώ”,就像有用的,但不是过于易懂(除非你深入了解该语言)。 - paxdiablo
显示剩余2条评论

5

我对大型列表的计数器、缩减和求和方法的性能很感兴趣。也许其他人也有兴趣。 你可以在这里查看:https://gist.github.com/torstenrudolf/277e98df296f23ff921c

我测试了这个字典列表的三种方法:

dictList = [{'a': x, 'b': 2*x, 'c': x**2} for x in xrange(10000)]

sum方法表现最佳,其次是reduce方法,而Counter方法最慢。下面显示的时间单位为秒。

In [34]: test(dictList)
Out[34]: 
{'counter': 0.01955194902420044,
 'reduce': 0.006518083095550537,
 'sum': 0.0018319153785705566}

但这取决于字典中元素的数量。相对于sum方法,reduce方法会更快地变慢。

l = [{y: x*y for y in xrange(100)} for x in xrange(10000)]

In [37]: test(l, num=100)
Out[37]: 
{'counter': 0.2401433277130127,
 'reduce': 0.11110662937164306,
 'sum': 0.2256883692741394}

2
这里有一个相当漂亮的网站。
final = {}
for k in dict1[0].Keys():
    final[k] = sum(x[k] for x in dict1)
return final

2
在Python 2.7中,你可以用collections.Counter对象替换字典。它支持计数器的加减操作。

1

这里是另一个可行的解决方案(python3),它非常通用,因为它适用于字典、列表和数组。对于非常见元素,原始值将包含在输出字典中。

def mergsum(a, b):
    for k in b:
        if k in a:
            b[k] = b[k] + a[k]
    c = {**a, **b}
    return c

dict1 = [{'a':2, 'b':3},{'a':3, 'b':4}]
print(mergsum(dict1[0], dict1[1]))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接