【问题标题】:Merge list of python dictionaries using multiple keys使用多个键合并python字典列表
【发布时间】:2018-10-04 11:19:14
【问题描述】:

我想使用多个键合并两个字典列表。

我有一个包含一组结果的字典列表:

l1 = [{'id': 1, 'year': '2017', 'resultA': 2},
      {'id': 2, 'year': '2017', 'resultA': 3},
      {'id': 1, 'year': '2018', 'resultA': 3},
      {'id': 2, 'year': '2018', 'resultA': 5}]

另外一组结果的字典列表:

l2 = [{'id': 1, 'year': '2017', 'resultB': 5},
      {'id': 2, 'year': '2017', 'resultB': 8},
      {'id': 1, 'year': '2018', 'resultB': 7},
      {'id': 2, 'year': '2018', 'resultB': 9}]

我想使用 'id' 和 'year' 键组合它们以获得以下信息:

all = [{'id': 1, 'year': '2017', 'resultA': 2, 'resultB': 5},
       {'id': 2, 'year': '2017', 'resultA': 3, 'resultB': 8},
       {'id': 1, 'year': '2018', 'resultA': 3, 'resultB': 7},
       {'id': 2, 'year': '2018', 'resultA': 5, 'resultB': 9}]

我知道要在一个键上组合两个字典列表,我可以使用这个:

l1 = {d['id']:d for d in l1} 

all = [dict(d, **l1.get(d['id'], {})) for d in l2]  

但它忽略了年份,提供了以下不正确的结果:

all = [{'id': 1, 'year': '2018', 'resultA': 3, 'resultB': 5},
       {'id': 2, 'year': '2018', 'resultA': 5, 'resultB': 8},
       {'id': 1, 'year': '2018', 'resultA': 3, 'resultB': 7},
       {'id': 2, 'year': '2018', 'resultA': 5, 'resultB': 9}]

像在 R 中一样处理这个,通过添加我要合并的第二个变量,我得到一个 KeyError:

l1 = {d['id','year']:d for d in l1} 

all = [dict(d, **l1.get(d['id','year'], {})) for d in l2]   

如何使用多个键进行合并?

【问题讨论】:

    标签: python dictionary merge


    【解决方案1】:

    您可以通过idyear 上的结果列表组合列表和分组。然后将具有相同键的字典合并在一起。

    使用itertools.groupby可以进行分组,使用collection.ChainMap可以进行合并

    >>> from itertools import groupby
    >>> from collections import ChainMap
    
    >>> [dict(ChainMap(*list(g))) for _,g in groupby(sorted(l1+l2, key=lambda x: (x['id'],x['year'])),key=lambda x: (x['id'],x['year']))]
    >>> [{'resultA': 2, 'id': 1, 'resultB': 5, 'year': '2017'}, {'resultA': 3, 'id': 1, 'resultB': 7, 'year': '2018'}, {'resultA': 3, 'id': 2, 'resultB': 8, 'year': '2017'}, {'resultA': 5, 'id': 2, 'resultB': 9, 'year': '2018'}]
    

    或者,为了避免lambda,您也可以使用operator.itemgetter

     >>> from operator import itemgetter
     >>> [dict(ChainMap(*list(g))) for _,g in groupby(sorted(l1+l2, key=itemgetter('id', 'year')),key=itemgetter('id', 'year'))]
    

    【讨论】:

      【解决方案2】:

      扩展@AlexHall's suggestion,你可以使用collections.defaultdict来帮助你:

      from collections import defaultdict
      
      d = defaultdict(dict)
      
      for i in l1 + l2:
          results = {k: v for k, v in i.items() if k not in ('id', 'year')}
          d[(i['id'], i['year'])].update(results)
      

      结果

      defaultdict(dict,
                  {(1, '2017'): {'resultA': 2, 'resultB': 5},
                   (1, '2018'): {'resultA': 3, 'resultB': 7},
                   (2, '2017'): {'resultA': 3, 'resultB': 8},
                   (2, '2018'): {'resultA': 5, 'resultB': 9}})
      

      【讨论】:

        【解决方案3】:

        使用元组 (d['id'], d['year']) 代替 d['id','year'] 作为键。

        【讨论】:

          猜你喜欢
          • 2016-02-22
          • 1970-01-01
          • 2021-01-10
          • 2013-11-02
          • 2020-04-24
          • 2012-08-11
          • 2012-07-17
          • 1970-01-01
          相关资源
          最近更新 更多