【问题标题】:grouping & summing similar values in python在python中对相似值进行分组和求和
【发布时间】:2018-07-09 09:56:22
【问题描述】:

我有这种格式的数据:

d = [
 {'key': '2018-05-10', 'vals': {'Clicks': 229, 'Link Clicks': 210}},
 {'key': '2018-05-11', 'vals': {'Clicks': 365, 'Link Clicks': 379}},

 {'key': '2018-05-10', 'vals': {'Clicks': 139, 'Link Clicks': 11}},
 {'key': '2018-05-11', 'vals': {'Clicks': 1348, 'Link Clicks': 73}},

]

即它有多个具有相同key的条目

我希望它对它进行分组,以便将 ClicksLink Clicks 汇总为共同日期:

所以输出应该是这样的:

d = [
 {'key': '2018-05-10', 'vals': {'Clicks': 368, 'Link Clicks': 221}},
 {'key': '2018-05-11', 'vals': {'Clicks': 1713, 'Link Clicks': 452}},
]

我想到了首先使用 defaultdict 将值组合在一起:

from collections import defaultdict

    dd = defaultdict(list)

    for i in d:                        
        dd[i['key']].append(i['vals'])

给出以下输出:

{ 2018-05-10': [
             {'Clicks': 229, 'Link Clicks': 210},
             {'Clicks': 139, 'Link Clicks': 11}
              ],
 '2018-05-11': [
             {'Clicks': 365, 'Link Clicks': 379},
             {'Clicks': 1348, 'Link Clicks': 73}
             ]}

现在我想我可以使用Counter 来总结价值,但我知道该怎么做。此外,键的名称,即 ClicksLink Clicks 可能会更改和 vals 可以有超过 2 个条目。

不使用defaultdict也可以吗?有没有更好的方法?

注意:我认为使用这种 defaultdict 方法并不好,因为我总是希望数据按日期排序,一旦我使用 dict 我就会失去顺序

【问题讨论】:

    标签: python dictionary counter defaultdict


    【解决方案1】:
    from pprint import pprint
    from collections import Counter, OrderedDict
    
    d = {
    '2018-05-10': [
                 {'Clicks': 229, 'Link Clicks': 210},
                 {'Clicks': 139, 'Link Clicks': 11}
                  ],
     '2018-05-11': [
                 {'Clicks': 365, 'Link Clicks': 379},
                 {'Clicks': 1348, 'Link Clicks': 73}
                 ],
    }
    
    m = OrderedDict()
    for k, v in d.items():
        m[k] = Counter()
        for i in v:
            m[k].update(i)
        m[k] = dict(m[k])
        # or if you want to keep the 'vals' key and list:
        # m[k] = [{"vals": dict(m[k])}]
    
    pprint(m)
    

    输出:

    OrderedDict([('2018-05-11', {'Clicks': 1713, 'Link Clicks': 452}),
                 ('2018-05-10', {'Clicks': 368, 'Link Clicks': 221})])
    

    【讨论】:

    • 但我希望日期始终按排序顺序排列。
    • 我编辑使用OrderedDict 对字典进行排序。
    • d 是在我的问题中使用defaultdict(list) 生成的,所以订单信息本身就丢失了?
    • 可能,取决于您使用的 python 版本。只有最新版本的 Python 才会默认对 dict 进行排序。
    • 那么我该如何解决?我使用的是 python 2.7 版
    【解决方案2】:

    您可以使用嵌套字典推导。相关的c_type 键,即ClicksLink Clicks,来自每个日期的第一个列表。否则,该方法自然会接受任意数量的类别。

    res = {k: {'vals': {c_type: sum(item[c_type] for item in v) for c_type in v[0]}}
           for k, v in dd.items()}
    
    {'2018-05-10': {'vals': {'Clicks': 368, 'Link Clicks': 221}},
     '2018-05-11': {'vals': {'Clicks': 1713, 'Link Clicks': 452}}}
    

    【讨论】:

    • 日期是否总是有序的?我需要它,所以输出应该是我猜的字典列表?我已经用这些信息更新了问题
    • @anekix,字典被认为是无序的(除非你有 Python 3.7+)。如果您使用的是 Python 3.6 或更低版本,则应使用collections.OrderedDict 定义订单。
    【解决方案3】:

    我建议您不要将输出格式作为每个字典只有键 (key:vals) 的字典列表,而应该只使用 {key: vals} 对的实际字典!

    这使代码更简洁、更易读,并且使访问特定日期更整洁,因为您无需循环访问列表 (O(n)),您可以直接访问该日期并获得点击次数。

    例如:

    dates = {}
    for dd in d:
        dates.setdefault(dd['key'], []).append(dd['vals'])
    
    dates = {k: {kk:sum(dd[kk] for dd in v) for kk in v[0].keys()} \
                                            for k,v in dates.items()}
    

    给出:

    {
      "2018-05-10": {
        "Clicks": 368,
        "Link Clicks": 221
      },
      "2018-05-11": {
        "Clicks": 1713,
        "Link Clicks": 452
      }
    }
    

    现在您可以通过以下方式直接获取特定日期的数据:

    dates['2018-05-11']['Clicks']
    #1713
    

    如果您需要排序字典的列表(按日期),那么我们可以只使用当前字典并索引原始数据中的每个日期,因为它们似乎已经排序:

    order = [dd['key'] for dd in d]
    date_list = sorted([{'key':k,'vals':v} for k,v in dates.items()], \
                                           key=lambda dd: order.index(dd['key']))
    

    date_list 作为日期排序列表:

    [
      {
        "key": "2018-05-10",
        "vals": {
          "Clicks": 368,
          "Link Clicks": 221
        }
      },
      {
        "key": "2018-05-11",
        "vals": {
          "Clicks": 1713,
          "Link Clicks": 452
        }
      }
    ]
    

    【讨论】:

    • 我希望数据始终按日期排序。我已经用这些信息更新了问题
    • @anekix 已更新 - 您现在可以使用 dates 字典快速访问特定日期或使用 date_list 对它们进行迭代 :)
    【解决方案4】:
    from collections import defaultdict, Counter, OrderedDict
    ld = [{'key': '2018-05-10', 'vals': {'Clicks': 229, 'Link Clicks': 210}}, {'key': '2018-05-11', 'vals': {'Clicks': 365, 'Link Clicks': 379}}, {'key': '2018-05-10', 'vals': {'Clicks': 139, 'Link Clicks': 11}}, {'key': '2018-05-11', 'vals': {'Clicks': 1348, 'Link Clicks': 73}}]
    out=defaultdict(Counter())
    for d in ld:
        out[d['key']].update(d['vals'])
    
    new = OrderedDict(sorted(out.items()))
    print(new)
    # OrderedDict([('2018-05-10', Counter({'Clicks': 368, 'Link Clicks': 221})), ('2018-05-11', Counter({'Clicks': 1713, 'Link Clicks': 452}))])
    

    【讨论】:

      【解决方案5】:

      我们可以将其概括为基本的“分组折叠”方法:

      from operator import add, itemgetter
      
      def group_fold(data, fold=add, key=itemgetter('key'), vals=itemgetter('vals')):
          result = {}
          for entry in data:
              ky = key(entry)
              vlb = vals(entry)
              vla = result.get(ky, None)
              if vla:
                  for subk, subv in vl.items():
                      if subk in vla:
                          vla[subk] = fold(vla[subk], subv)
                      else:
                          vla[subk] = subv
              else:
                  result[ky] = dict(vlb)
          return result
      

      因此,我们现在可以将其用作group_fold(d),但我们可以自定义折叠功能,例如使用multiply 而不是add

      from operator import mul
      
      group_fold(d, fold=mul)
      

      【讨论】:

        【解决方案6】:

        试试这个解决方案:

        d = [
        {'key': '2018-05-10', 'vals': {'Clicks': 229, 'Link Clicks': 210}},
        {'key': '2018-06-01', 'vals': {'Clicks': 365, 'Link Clicks': 379}},
        
        {'key': '2018-05-10', 'vals': {'Clicks': 139, 'Link Clicks': 11}},
        {'key': '2018-06-01', 'vals': {'Clicks': 1348, 'Link Clicks': 73}},
        
        ]
        
        final_dict = {}
        
        for doc in d:
            date = doc['key']
        
            if date not in final_dict:
                final_dict[date] = {}
        
                for key in doc['vals']:
                    final_dict[date][key] = doc['vals'][key]
        
            else:
        
                for key in doc['vals']:
                    final_dict[date][key] += doc['vals'][key]
        
        
        resp_dict = [{date: final_dict[date]} for date in sorted(final_dict)]
        
        print resp_dict
        

        【讨论】:

          【解决方案7】:

          使用嵌套的默认字典:

          result = defaultdict(lambda: defaultdict(int))
          for entry in d:
            for key, val in entry['vals'].items():
              result[entry['key']][key] += val
          

          它会给你这个结果:

          {"2018-05-10": {"Clicks": 368, "Link Clicks": 221}, "2018-05-11": {"Clicks": 1713, "Link Clicks": 452}}
          

          【讨论】:

          • 查看我的编辑问题。日期应按原始数据中的顺序排列。使用dictionary 将丢失订单信息
          【解决方案8】:

          使用itertools.groupby

          d =  [
           {'key': '2018-05-10', 'vals': {'Clicks': 368, 'Link Clicks': 221}},
           {'key': '2018-05-11', 'vals': {'Clicks': 1713, 'Link Clicks': 452}},
          ]
          
          from itertools import groupby
          from operator import itemgetter
          newdict={}
          for dt, k in groupby(sorted(d,key=itemgetter('key')),key=itemgetter('key')):
              for d in k:
                  newdict[dt]=d['vals']
          

          输出:

          {'2018-05-10': {'Clicks': 368, 'Link Clicks': 221},
           '2018-05-11': {'Clicks': 1713, 'Link Clicks': 452}}
          

          【讨论】:

          • 查看我的问题中的编辑。日期应该是有序的
          • @min2bro: 但是在这里你保留最后一次出现,你总结这些值吗?
          猜你喜欢
          • 2011-01-14
          • 2012-04-12
          • 1970-01-01
          • 2020-12-12
          • 2018-03-15
          • 2023-01-04
          • 1970-01-01
          • 1970-01-01
          • 2011-02-15
          相关资源
          最近更新 更多