【问题标题】:Python - Sum the value in the list of dictionary based on the same keyPython - 根据相同的键对字典列表中的值求和
【发布时间】:2018-05-25 07:20:09
【问题描述】:

我有一个字典列表,如下所示:

data = [{'stat3': '5', 'stat2': '4', 'player': '1'}, 
        {'stat3': '8', 'stat2': '1', 'player': '1'}, 
        {'stat3': '6', 'stat2': '1', 'player': '3'}, 
        {'stat3': '3', 'stat2': '7', 'player': '3'}]

我想得到一个嵌套字典,其键是键('player')中的值,其值是聚合统计信息的字典。

输出应该:

{'3': {'stat3': 9, 'stat2': 8, 'player': '3'}, 
 '1': {'stat3': 13, 'stat2': 5, 'player': '1'}}

以下是我的代码:

from collections import defaultdict
result = {}
total_stat = defaultdict(int)

for dict in data:
    total_stat[dict['player']] += int(dict['stat3'])  
    total_stat[dict['player']] += int(dict['stat2']) 
total_stat = ([{'player': info, 'stat3': total_stat[info],
                'stat2': total_stat[info]} for info in 
                 sorted(total_stat, reverse=True)])
for item in total_stat:       
    result.update({item['player']: item})
print(result)

但是,我得到了这个:

{'3': {'player': '3', 'stat3': 17, 'stat2': 17}, 
 '1': {'player': '1', 'stat3': 18, 'stat2': 18}}

我怎样才能使它正确?还是有其他方法?

【问题讨论】:

  • 附带说明,您似乎想要一个命名元组来存储数据,而不是 dict。
  • 'stat3': total_stat[info], 'stat2': total_stat[info]- 当然是同一个值

标签: python python-3.x list dictionary


【解决方案1】:

只需使用更多嵌套的默认工厂:

>>> total_stat = defaultdict(lambda : defaultdict(int))
>>> value_fields = 'stat2', 'stat3'
>>> for datum in data:
...     player_data = total_stat[datum['player']]
...     for k in value_fields:
...         player_data[k] += int(datum[k])
...
>>> from pprint import pprint
>>> pprint(total_stat)
defaultdict(<function <lambda> at 0x1023490d0>,
            {'1': defaultdict(<class 'int'>, {'stat2': 5, 'stat3': 13}),
             '3': defaultdict(<class 'int'>, {'stat2': 8, 'stat3': 9})})

【讨论】:

    【解决方案2】:

    这里的大多数解决方案都使问题变得过于复杂。让我们让它更简单,更具可读性。给你:

    In [26]: result = {}
    
    In [27]: req_key = 'player'
    
    In [29]: for dct in data:
        ...:     player_val = dct.pop(req_key)
        ...:     result.setdefault(player_val, {req_key: player_val})
        ...:     for k, v in dct.items():
        ...:         result[player_val][k] = result[player_val].get(k, 0) + int(v)
    
    In [30]: result
    Out[30]:
    {'1': {'player': '1', 'stat2': 5, 'stat3': 13},
     '3': {'player': '3', 'stat2': 8, 'stat3': 9}}
    

    这里简单干净。对于这个简单的问题,不需要导入。现在进入程序:

    result.setdefault(player_val, {'player': player_val})
    

    如果结果中没有这样的键,则将默认值设置为"player": 3"player": 1

    result[player_val][k] = result[player_val].get(k, 0) + int(v)
    

    这会将具有共同值的键的值相加。

    【讨论】:

      【解决方案3】:

      不是最好的代码,也不是更pythonic,但我认为你应该能够浏览它并找出你的代码哪里出错了。

      def sum_stats_by_player(data):
          result = {}
      
          for dictionary in data:
              print(f"evaluating dictionary {dictionary}")
      
              player = dictionary["player"]
              stat3 = int(dictionary["stat3"])
              stat2 = int(dictionary["stat2"])
      
              # if the player isn't in our result
              if player not in result:
                  print(f"\tfirst time player {player}")
                  result[player] = {}  # add the player as an empty dictionary
                  result[player]["player"] = player
      
              if "stat3" not in result[player]:
                  print(f"\tfirst time stat3 {stat3}")
                  result[player]["stat3"] = stat3
              else:
                  print(f"\tupdating stat3 { result[player]['stat3'] + stat3}")
                  result[player]["stat3"] += stat3
      
              if "stat2" not in result[player]:
                  print(f"\tfirst time stat2 {stat2}")
                  result[player]["stat2"] = stat2
              else:
                  print(f"\tupdating stat2 { result[player]['stat2'] + stat2}")
                  result[player]["stat2"] += stat2
      
          return result
      
      
      data = [{'stat3': '5', 'stat2': '4', 'player': '1'},
              {'stat3': '8', 'stat2': '1', 'player': '1'},
              {'stat3': '6', 'stat2': '1', 'player': '3'},
              {'stat3': '3', 'stat2': '7', 'player': '3'}]
      
      print(sum_stats_by_player(data))
      

      【讨论】:

        【解决方案4】:

        使用计数器的另一个版本

        import itertools
        from collections import Counter
        
        def count_group(group):
            c = Counter()
            for g in group:
                g_i = dict([(k, int(v)) for k, v in g.items() if k != 'player'])
                c.update(g_i)
            return dict(c)
        
        sorted_data = sorted(data, key=lambda x:x['player'])
        results = [(k, count_group(g)) for k, g in itertools.groupby(sorted_data, lambda x: x['player'])]
        
        print(results)
        

        给予

        [('1', {'stat3': 13, 'stat2': 5}), ('3', {'stat3': 9, 'stat2': 8})]
        

        【讨论】:

        • 注意:要使groupby 起作用,data 列表需要按subdict['player'] 排序
        【解决方案5】:

        您的数据是一个 DataFrame,一个自然的pandas 解决方案是:

        In [34]: pd.DataFrame.from_records(data).astype(int).groupby('player').sum().T.to_dict()
        
        Out[34]: {1: {'stat2': 5, 'stat3': 13}, 3: {'stat2': 8, 'stat3': 9}}
        

        【讨论】:

        • 你可以稍微清理一下。 astype(int)applymap 快​​得多(并且更易于阅读),并且有一个 orient='index' 可以获取自 0.17.0 版本以来指定的输出格式。所以pd.DataFrame.from_records(data).astype(int).groupby('player').sum().to_dict(orient='index').
        【解决方案6】:

        两个循环可以让你:

        1. 按主键对数据进行分组
        2. 汇总所有辅助信息

        这两个任务是在如下所示的aggregate_statistics函数中完成的。

        from collections import Counter
        from pprint import pprint
        
        
        def main():
            data = [{'player': 1, 'stat2': 4, 'stat3': 5},
                    {'player': 1, 'stat2': 1, 'stat3': 8},
                    {'player': 3, 'stat2': 1, 'stat3': 6},
                    {'player': 3, 'stat2': 7, 'stat3': 3}]
            new_data = aggregate_statistics(data, 'player')
            pprint(new_data)
        
        
        def aggregate_statistics(table, key):
            records_by_key = {}
            for record in table:
                data = record.copy()
                records_by_key.setdefault(data.pop(key), []).append(Counter(data))
            new_data = []
            for second_key, value in records_by_key.items():
                start, *remaining = value
                for record in remaining:
                    start.update(record)
                new_data.append(dict(start, **{key: second_key}))
            return new_data
        
        
        if __name__ == '__main__':
            main()
        

        【讨论】:

          【解决方案7】:

          此解决方案使用嵌套字典。 out 是一个 {player: Counter} 字典,而 Counter 本身是另一个字典 {stat: score}

          import collections
          
          def split_player_stat(dict_object):
              """
              Split a row of data into player, stat
          
              >>> split_player_stat({'stat3': '5', 'stat2': '4', 'player': '1'})
              '1', {'stat3': 5, 'stat2': 4}
              """
              key = dict_object['player']
              value = {k: int(v) for k, v in dict_object.items() if k != 'player'}
              return key, value
          
          data = [{'stat3': '5', 'stat2': '4', 'player': '1'},
                  {'stat3': '8', 'stat2': '1', 'player': '1'},
                  {'stat3': '6', 'stat2': '1', 'player': '3'},
                  {'stat3': '3', 'stat2': '7', 'player': '3'}]
          
          out = collections.defaultdict(collections.Counter)
          for player_stat in data:
              player, stat = split_player_stat(player_stat)
              out[player].update(stat)
          print(out)
          

          这个解决方案的神奇之处在于 collections.defaultdictcollections.Counter 类,它们的行为都像字典。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2019-06-15
            • 2014-02-26
            • 1970-01-01
            • 1970-01-01
            • 2020-08-05
            • 2019-09-21
            • 2015-05-17
            相关资源
            最近更新 更多