字典值列表求和

7

I have a list of dictionary in this form :

[
{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8}, 

{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},

{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},
]

我希望按照键将此列表中的值相加,使其对于列表中的每个元素如下:

    {
     'signal_8': 3,
     'signal_1': 21,
     'signal_10': 15,
     'signal_5': 6,
     'signal_2': 15,
     'signal_6': 9,
     'signal_4': 27,
     'signal_3': 18,
     'signal_9': 12,
     'signal_7': 24
    }

我尝试的方法如下:
    result = {}
    sm = 0
    for elm in original_list:
        for k,v in elm.items():
            sm += v
            result[k] = sm
    print(result)

但是它仍然无法正常工作。

为什么会奏效?你只有一个数字来表示所有不同的信号。请查看collections.Counter.defaultdict - jonrsharpe
我不理解你代码中的 test 是什么意思。你能否请提供一个最小化、完整化且可验证的示例 - daveruinseverything
@daveruinseverything 抱歉,那只是一个拼写错误,我现在已经更正了。 - saul
7个回答

13

类似于daveruinseverything的回答,我会使用Counter来解决这个问题,但是利用它的update方法。

signals成为您的字典列表。

>>> from collections import Counter
>>> c = Counter()
>>> for d in signals:
...     c.update(d)
... 
>>> c
Counter({'signal_4': 27, 'signal_7': 24, 'signal_1': 21, 'signal_3': 18, 'signal_10': 15, 'signal_2': 15, 'signal_9': 12, 'signal_6': 9, 'signal_5': 6, 'signal_8': 3})
为了OP的利益,你能简要描述一下这里正在发生什么吗?
一个Counter类似于一个dict,但它的update方法将值添加到预先存在的键的值中,而不是覆盖它们。

点赞这个回答,因为这可能是我在实际代码中使用的解决方案。为了楼主的利益,您能简要描述一下这里发生了什么吗? - daveruinseverything

3
你的代码问题在于无论 key 是什么,你都在对 `sm` 和 `v` 求和。下面是一个经过重新格式化的可以正常工作的代码版本。它只需将列表中每个元素的值添加到结果对象中:
from collections import defaultdict

result = defaultdict(int)

for elm in original_list:
    for k, v in elm.items():
        result[k] += v
print(result)

或者,只需一行代码即可:
result = {key: sum(e[key] for e in original_list) for key in original_list[0].keys()}

保留它可以提高可读性,但值得注意的是,迭代字典与迭代其键相同,因此在这里可以删除.keys() - fuglede
@fuglede,这是一个有效的观察。使用.keys()是个人偏好。 - iulian

2
你需要的是 Counter 集合类型。Python 关于 collections 的文档描述得最清楚,但实质上 Counter 是一种特殊的字典类型,其中所有的值都是整数。你可以传入任何键(包括不存在的键)并对它们进行加法操作。例如:
from collections import Counter

original_list = [
    {'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8}, 
    {'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},
    {'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},
]

result = Counter()

for elem in original_list:
    for key, value in elem.items():
        result[key] += value

print(result)

编辑:@timgeb提供了这个答案的变体,该变体在Counter对象上本地使用update()方法。我建议这是最好的答案。


2

使用itertools.groupby,你可以像这样做:

merged_list = sorted(p for l in original_list for p in l.items())
groups = groupby(merged_list, key=lambda p: p[0])
result = {signal: sum(pair[1] for pair in pairs) for signal, pairs in groups}

如果您可以假设每个字典都包含完全相同的键,则上述内容可以简化为:
{k: sum(d[k] for d in original_list) for k in original_list[0]}

注意,数据分析库pandas使得这样的操作变得微不足道。
In [70]: import pandas as pd

In [72]: pd.DataFrame(original_list).sum()
Out[72]:
signal_1     21
signal_10    15
signal_2     15
signal_3     18
signal_4     27
signal_5      6
signal_6      9
signal_7     24
signal_8      3
signal_9     12
dtype: int64

这是一个正确的解决方案,但我不一定推荐使用。它很难阅读/解析,并且难以维护。 - daveruinseverything
我并不反对;如果我要在任何共享代码中使用它,我也会将其分开一点;让我们继续这样做。 - fuglede

1
你当前的代码使用一个累加总和来处理所有信号,而实际上你需要为每个信号分别使用一个单独的总和。
如果你想让原始代码正常运行,你需要首先检查 result 中是否存在该键,并在不存在时提前将其初始化为 0。然后为相应的键累加总和。 代码:
result = {}
for elm in original_list:
    for k, v in elm.items():

        # Initialise it if it doesn't exist
        if k not in result:
            result[k] = 0

        # accumulate sum seperately 
        result[k] += v

print(result)

输出:

{'signal_9': 12, 'signal_8': 3, 'signal_1': 21, 'signal_3': 18, 'signal_2': 15, 'signal_5': 6, 'signal_4': 27, 'signal_7': 24, 'signal_6': 9, 'signal_10': 15}

注意:正如其他人所展示的那样,为了避免自我初始化,你可以使用collections.defaultdict()collections.Counter()


0

试试这个:

original_list = [
{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8}, 

{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},

{'signal_8': 1, 'signal_1': 7, 'signal_10': 5, 'signal_5': 2, 'signal_2': 5, 'signal_6': 3, 'signal_4': 9, 'signal_3': 6, 'signal_9': 4, 'signal_7': 8},
]
print({k:sum([x[k] for x in original_list if k in x]) for i in original_list for k,v in i.items()})

输出:

{'signal_8': 3, 'signal_1': 21, 'signal_10': 15, 'signal_5': 6, 'signal_2': 15, 'signal_6': 9, 'signal_4': 27, 'signal_3': 18, 'signal_9': 12, 'signal_7': 24}

请注意,如果有缺失的信号,它将被视为零。

为什么要将字典的键和值一起压缩(zip)呢?这就是items方法的作用,而且OP已经在使用了。另外,你不需要创建一个列表,因为你可以迭代一个zip对象。 - jonrsharpe
@jonrsharpe 现在好多了。 - U13-Forward
@U8-Forward,从你的示例中不清楚哪个变量代表原始列表。也许将“d”更改为“original_list”,这样就更清楚你的代码在做什么,以及Op应该如何测试它。 - daveruinseverything
1
这个答案的复杂度很糟糕(O(n^2 * m),其中n是字典数量,m是每个字典中的信号数量;它会多次计算相同的值,只有最后一个才会出现在输出中),可读性也不好,如果任何一个字典缺少任何信号,它将抛出错误。@saul你应该再看看其他的。 - jonrsharpe
1
当每个字典中的键不同时,这将失败。如果它们确实相同,则可以避免循环遍历所有字典,而只需使用 original_list[0]。还要注意,由于您从未使用值,因此 k,v in i.items() 可以简化为 k in i,并且您不需要创建值列表来对其进行求和。 - fuglede
显示剩余5条评论

0

您能否尝试以下代码

basedict=siglist[0]
for k in basedict.keys():
    result=[currdict[k] for currdict in siglist]
    endval=sum(result)
    print("Key %s and sum of values %d"%(k,endval))

输出

Key signal_9 and sum of values 12
Key signal_2 and sum of values 15
Key signal_8 and sum of values 3
Key signal_5 and sum of values 6
Key signal_7 and sum of values 24
Key signal_10 and sum of values 15
Key signal_1 and sum of values 21
Key signal_6 and sum of values 9
Key signal_4 and sum of values 27
Key signal_3 and sum of values 18

注意:- 由于我们确定所有字典中的键都相同,因此此解决方案有效。如果您有一个具有不匹配元素的字典,则会导致KeyError。因此请注意此限制。

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