【问题标题】:Python - Summing a list of dictionaries with a conditionPython - 对具有条件的字典列表求和
【发布时间】:2013-04-14 11:38:15
【问题描述】:

假设我有一个清单:

l = [{"num1":3, "num2":8, "num3":5, "type":"A"}, {"num1":2, "num2":5, "num3":5, "type":"B"}, {"num1":5, "num2":2, "num3":1, "type":"A"}, {"num1":4, "num2":4, "num3":9, "type":"B"}

我想创建 2 个字典: 总和:

{"num1":8, "num2":10, "num3":6}

总和:

{"num1":6, "num2":9, "num3":14}

我想让它尽可能简单易读。 我设法以一种可怕的方式使用太多变量来做到这一点......

谢谢!

【问题讨论】:

  • 列表中是否总是有 4 个字典,每个字典中是否总是只有 3 个数字?
  • “我用太多变量以一种可怕的方式设法做到了……”那你为什么不把这段代码粘贴到这里呢?
  • @lonut Hulub - 数字始终相同(4 和 3 只是示例)

标签: python dictionary sum conditional-statements


【解决方案1】:

这可以使用一些list and dict comprehensions 相当容易地完成。

from operator import itemgetter
from itertools import groupby

l = [{"num1": 3, "num2": 8, "num3": 5, "type": "A"},
     {"num1": 2, "num2": 5, "num3": 5, "type": "B"},
     {"num1": 5, "num2": 2, "num3": 1, "type": "A"},
     {"num1": 4, "num2": 4, "num3": 9, "type": "B"}]

wanted_values = {"num1", "num2", "num3"}

type_getter = itemgetter("type")
groups = [(group, list(items)) for group, items in 
          groupby(sorted(l, key=type_getter), type_getter)]

print({group: {k: sum(map(itemgetter(k), items)) for k in wanted_values}
       for group, items in groups})

这给了我们:

{'B': {'num2': 9, 'num3': 14, 'num1': 6}, 
 'A': {'num2': 10, 'num3': 6, 'num1': 8}}

我们按类型对值进行排序,然后使用 itertools.groupby() 将它们分成组(制作项目列表而不是生成器,因为我们需要多次迭代它们)。

然后我们使用嵌套的 dict 推导式来创建我们需要的数据,将项目中的值相加并将它们分配给一个组。

这是一个灵活的解决方案,可以扩展到不止两种类型。

【讨论】:

  • 你运行了这段代码吗?我在尝试运行时遇到 TypeError。
  • @segfolt 我(不小心)在 2.x 中运行了这个 - 它似乎在 3.x 中不起作用,让我解决这个问题。
  • @gilgil28 这个版本应该可以在 2.x 和 3.x 中正常工作,如果需要可以使用浮点值。
【解决方案2】:

我会使用嵌套理解。

lst = [{"num1": 3, "num2": 8, "num3": 5, "type": "A"},
     {"num1": 2, "num2": 5, "num3": 5, "type": "B"},
     {"num1": 5, "num2": 2, "num3": 1, "type": "A"},
     {"num1": 4, "num2": 4, "num3": 9, "type": "B"}]

sum_by_a = {key: sum(d[key] for d in lst if d['type'] == 'A')
    for key in ("num1", "num2", "num3")}

或者,对于泛型,

sum_by_type = lambda t: {
    key: sum(d[key] for d in lst if d['type'] == t)
        for key in ("num1", "num2", "num3")}

 sum_by_b = sum_by_type(B)

【讨论】:

  • @gilgil28:选我!选我!没有廉价进口,100% 本地制造!
  • lambda 是一种将简单函数内联的简单方法 - 在我看来,这里完全不合适,因为 def 会更清楚地完成相同的工作。
【解决方案3】:

我会这样做,使用Collections.Counter

from collections import Counter
from functools import reduce

l = [{"num1":3, "num2":8, "num3":5, "type":"A"},
     {"num1":2, "num2":5, "num3":5, "type":"B"},
     {"num1":5, "num2":2, "num3":1, "type":"A"},
     {"num1":4, "num2":4, "num3":9, "type":"B"}]

def remove_keys(d, keys):
    return {i: j for i, j in d.items() if i not in keys}

def add_dicts(dicts):
    return reduce(lambda a, b: a+b,  map(Counter, dicts))

tmp = {}
for d in l:
    tmp[d["type"]] = tmp.get(d["type"], []) + [remove_keys(d, ["type"])]

result = {key: add_dicts(value) for key, value in dict(tmp).items()}

给出:

{'A': Counter({'num2': 10, 'num1': 8, 'num3': 6}), 
'B': Counter({'num3': 14, 'num2': 9, 'num1': 6})}

【讨论】:

    猜你喜欢
    • 2021-09-04
    • 2017-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-27
    • 1970-01-01
    • 1970-01-01
    • 2018-06-30
    相关资源
    最近更新 更多