【问题标题】:How to sum a list of dicts如何总结字典列表
【发布时间】:2018-04-18 00:12:31
【问题描述】:

获取字典列表并总结列表中每一行中匹配键的所有值的最 Pythonic 方式是什么?

我这样做了,但我怀疑理解更像 Pythonic:

from collections import defaultdict
demandresult = defaultdict(int)   # new blank dict to store results 
for d in demandlist:
    for k,v in d.iteritems():
        demandresult[k] = demandresult[k] + v

Python - sum values in dictionary 中,问题始终涉及相同的键,但就我而言,每一行中的键可能是以前从未遇到过的新键。

【问题讨论】:

  • 你能帮我理解吗,demandlist 是什么,一个字典列表,其值以某种方式有行?能举个例子吗?
  • 这里有3行需求列表{u'2018-04-29': 1, u'2018-04-30': 1, u'2018-05-01': 1} {u '2018-04-21': 1} {u'2018-04-18': 1, u'2018-04-19': 1, u'2018-04-17': 1}
  • 知道了。您添加重复键的值这一事实让我强烈地想到了reduction,它是表达任何此类组合(不仅仅是添加)的通用工具。
  • 这个解决方案完全没问题。也许只是demandresult[k] += v

标签: python dictionary


【解决方案1】:

我认为你的方法很pythonic。理解很好,但它们不应该过度,它们会导致非常混乱的单行,如下面的:)。

如果你坚持使用 dict comp:

demand_list = [{u'2018-04-29': 1, u'2018-04-30': 1, u'2018-05-01': 1}, 
               {u'2018-04-21': 1},
               {u'2018-04-18': 1, u'2018-04-19': 1, u'2018-04-17' : 1}]

d = {key:sum(i[key] for i in demand_list if key in i) 
     for key in set(a for l in demand_list for a in l.keys())}

print(d)
>>>{'2018-04-21': 1, '2018-04-17': 1, '2018-04-29': 1, '2018-04-30': 1, '2018-04-19': 1, '2018-04-18': 1, '2018-05-01': 1}

【讨论】:

  • 这个 dict comp 在处理列表中的 494 个元素后确实产生了与我原来问题中的 for 循环相同的输出。
  • 可以,但 for 循环更干净,应该更快。
  • 我确实喜欢 dict 理解——创建一组所有键,然后为每个键搜索列表中的条目并将它们相加,虽然复杂但很酷——但是是的,它会很慢因为您循环数据的次数超出了您的需要(意外地二次)。一个合理的妥协可能是用户itertools.chain?
  • 去掉sum()set()的函数调用中的括号;他们强制 Python 完成创建列表的中间步骤,然后将其传递给函数,而不是允许函数直接使用生成器表达式。
【解决方案2】:

这是另一个使用 collections.ChainMap 获取组合键的单行 (ab-):

>>> from collections import ChainMap
>>> {k: sum(d.get(k, 0) for d in demand_list) for k in ChainMap(*demand_list)}
{'2018-04-17': 1, '2018-04-21': 1, '2018-05-01': 1, '2018-04-30': 1, '2018-04-19': 1, '2018-04-29': 1, '2018-04-18': 1}

这很容易成为这里提出的方法中最慢的。

【讨论】:

    【解决方案3】:

    您的代码中唯一不清楚的是双循环。将demandlist 折叠成一个扁平的可迭代对象可能更清楚——然后loopant 会尽可能简单地呈现逻辑。考虑:

    demandlist = [{
        u'2018-04-29': 1,
        u'2018-04-30': 1,
        u'2018-05-01': 1
    }, {
        u'2018-04-21': 1
    }, {
        u'2018-04-18': 1,
        u'2018-04-19': 1,
        u'2018-04-17': 1
    }]
    
    import itertools as it
    from collections import defaultdict
    
    demandresult = defaultdict(int)
    
    for k, v in it.chain.from_iterable(map(lambda d: d.items(), demandlist)):
        demandresult[k] = demandresult[k] + v
    

    (有了这个,print(demandresult) 打印出defaultdict(<class 'int'>, {'2018-04-29': 1, '2018-04-30': 1, '2018-05-01': 1, '2018-04-21': 1, '2018-04-18': 1, '2018-04-19': 1, '2018-04-17': 1})。)

    想象自己第一次阅读这篇文章(或几个月后),我可以看到自己在想,“好吧,我正在将 demandlist 折叠成一个 key-val 迭代,我并不特别关心如何,然后对匹配键的值求和。”

    不幸的是,我需要map 来确保最终的迭代具有键值对……it.chain.from_iterable(demandlist) 是一个仅键迭代,所以我需要在每个字典上调用 items

    请注意,与提出的许多答案不同,此实现(就像您的一样!)将数据扫描次数减少到只有一次——性能优势(我尝试尽可能多地获得简单的性能优势)。

    【讨论】:

      【解决方案4】:

      我想您想返回每个字典的总和值列表。

      list_of_dict = [
          {'a':1, 'b':2, 'c':3},
          {'d':4, 'e':5, 'f':6}
      ]
      
      sum_of_each_row = [sum(v for v in d.values()) for d in list_of_dict] # [6,15]
      

      如果要返回总和,只需简单地将 sum() 包装到“sum_of_each_row”即可。

      编辑:

      主要问题是你没有每个键的默认值,所以你可以使用方法 dict.setdefault() 在有新键时设置默认值。

      list_of_dict = [
          {'a':1, 'b':1},
          {'b':1, 'c':1},
          {'a':2}
      ]
      
      d = {}
      d = {k:d[k]+v if k in d.keys() else d.setdefault(k,v)
          for row in list_of_dict for k,v in row.items()} # {'a':3, 'b':2, 'c':1}
      

      【讨论】:

      • 从您的示例中,我的目标是获取第 1 行中键 'a' 的 '1' 值,当我在后续行中遇到键 'a' 时,将这个 '1' 相加无论下一次出现的“a”包含什么值。所以这是一个关键匹配和求和问题。我编辑了原始问题以使其更清楚。
      • 现在完全了解您的问题。请查看我的编辑;)
      猜你喜欢
      • 1970-01-01
      • 2023-01-19
      • 2022-10-08
      • 1970-01-01
      • 2023-01-11
      • 1970-01-01
      • 2018-11-19
      • 2020-09-15
      • 1970-01-01
      相关资源
      最近更新 更多