繁体   English   中英

用每个字典的唯一键展平嵌套字典?

[英]Flattening a nested dictionary with unique keys for each dictionary?

我有一本具有以下格式的字典:

´´´{'7453': 
      {'2H': 
         {'1155': 
            {'in': [{'playerId': 281253}, {'playerId': 169212}], 
            'out': [{'playerId': 449240}, {'playerId': 257943}]},
          '2011': 
            {'in': [{'playerId': 449089}], 
            'out': [{'playerId': 69374}]}, 
          '2568': 
            {'in': [{'playerId': 481900}], 
            'out': [{'playerId': 1735}]}}}, 
    '7454': 
       {'1H': 
          {'2833': 
             {'in': [{'playerId': 56390}], 
             'out': [{'playerId': 208089}]}}, 
        '2H': 
          {'687': 
             {'in': [{'playerId': 574}], 
             'out': [{'playerId': 578855}]}, 
          '1627': 
             {'in': [{'playerId': 477400}], 
             'out': [{'playerId': 56386}]}, 
          '2725': 
             {'in': [{'playerId': 56108}], 
             'out': [{'playerId': 56383}]}}}}
´´´

我需要以下格式的数据(df): https://i.stack.imgur.com/GltRb.png

这意味着我想展平我的数据,以便我有 id:“7453”,一半:“H2”,分钟:“2011”,类型:“out”,playerId:“281253”。 另外,我需要每个玩家一个记录,但仍然包含所有其他数据(id、half 等)

我已经为此苦苦挣扎了好几天,似乎无法为这个特定问题找到任何解决方案。 到目前为止,我已经能够使用 pd.json_normalize() 或 flatten_json() 来解决它。 但在这种情况下,它不适合我。 如果有人能指出我正确的方向或编写一些可以解决此问题的代码,将不胜感激!

仅供参考:我最大的困难是我实际上需要一个标题/列来存放我的键。

pandas 已经explode展开列表,但我不知道字典的方法。

由于您的字典结构非常好,您可以尝试

[28]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack().apply(pd
    ...: .Series).stack().explode().apply(pd.Series).reset_index().rename(column
    ...: s={'level_0': 'teamId', 'level_1': 'matchPeriod', 'level_2': 'eventSec'
    ...: , 'level_3': 'type'})                                                  
Out[28]: 
   teamId matchPeriod eventSec type  playerId
0    7453          2H     1155   in    281253
1    7453          2H     1155   in    169212
2    7453          2H     1155  out    449240
3    7453          2H     1155  out    257943
4    7453          2H     2011   in    449089
..    ...         ...      ...  ...       ...
11   7454          2H     1627  out     56386
12   7454          2H     2725   in     56108
13   7454          2H     2725  out     56383
14   7454          1H     2833   in     56390
15   7454          1H     2833  out    208089

尽管将Series构造函数和stack链接起来非常难看,但会逐级构建 DataFrame。

更新:原则上,您可以将字典传递给DataFrameSeries构造函数

In [2]: d                                                                                                                                                                                                  
Out[2]: 
{'7453': {'2H': {'1155': {'in': [{'playerId': 281253}, {'playerId': 169212}],
    'out': [{'playerId': 449240}, {'playerId': 257943}]},
   '2011': {'in': [{'playerId': 449089}], 'out': [{'playerId': 69374}]},
   '2568': {'in': [{'playerId': 481900}], 'out': [{'playerId': 1735}]}}},
 '7454': {'1H': {'2833': {'in': [{'playerId': 56390}],
    'out': [{'playerId': 208089}]}},
  '2H': {'687': {'in': [{'playerId': 574}], 'out': [{'playerId': 578855}]},
   '1627': {'in': [{'playerId': 477400}], 'out': [{'playerId': 56386}]},
   '2725': {'in': [{'playerId': 56108}], 'out': [{'playerId': 56383}]}}}}

In [3]: pd.DataFrame(d)                                                                                                                                                                                    
Out[3]: 
                        7453                      7454
2H  {'1155': {'in': [{'pl...  {'687': {'in': [{'pla...
1H                       NaN  {'2833': {'in': [{'pl...

In [4]: pd.Series(d)                                                                                                                                                                                       
Out[4]: 
7453    {'2H': {'1155': {'in'...
7454    {'1H': {'2833': {'in'...
dtype: object

由于它们分别是二维和一维数据结构,因此它们还期望字典分别具有 2 级和 1 级深度嵌套。 DataFrame将您的“teamId”解释为索引,将“matchPeriod”解释为列,值是字典的值,如

In [5]: d['7453']['2H']                                                                                                                                                                                    
Out[5]: 
{'1155': {'in': [{'playerId': 281253}, {'playerId': 169212}],
  'out': [{'playerId': 449240}, {'playerId': 257943}]},
 '2011': {'in': [{'playerId': 449089}], 'out': [{'playerId': 69374}]},
 '2568': {'in': [{'playerId': 481900}], 'out': [{'playerId': 1735}]}}

Series的行为方式相同,但只有一个级别。

In [6]: d['7453']                                                                                                                                                                                          
Out[6]: 
{'2H': {'1155': {'in': [{'playerId': 281253}, {'playerId': 169212}],
   'out': [{'playerId': 449240}, {'playerId': 257943}]},
  '2011': {'in': [{'playerId': 449089}], 'out': [{'playerId': 69374}]},
  '2568': {'in': [{'playerId': 481900}], 'out': [{'playerId': 1735}]}}}

是你的第一级。 现在这又是一个字典,所以你也可以将它传递给Series构造函数

In [7]: pd.Series(d['7453'])                                                                                                                                                                               
Out[7]: 
2H    {'1155': {'in': [{'pl...
dtype: object

apply function 允许您对Series的每一行执行此操作

In [8]: pd.Series(d).apply(pd.Series)                                                                                                                                                                      
Out[8]: 
                            2H                        1H
7453  {'1155': {'in': [{'pl...                       NaN
7454  {'687': {'in': [{'pla...  {'2833': {'in': [{'pl...

现在您得到与DataFrame构造函数相同的结果。 这称为广播。 原始Series no 的每个值都成为其自己的Series ,并且索引用作列标签。 通过调用stack你 intead 告诉 pandas 给你一个系列 intead 并在需要时将所有标签堆叠到MultiIndex

In [9]: pd.Series(d).apply(pd.Series).stack()                                                                                                                                                              
Out[9]: 
7453  2H    {'1155': {'in': [{'pl...
7454  2H    {'687': {'in': [{'pla...
      1H    {'2833': {'in': [{'pl...
dtype: object

现在您再次拥有一个 Series(带有 2d 索引),其中每个值都是一个字典,可以再次将其传递给Series构造函数。 所以如果你重复这个apply(pd.Series).stack()链,你会得到

In [10]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack()                                                                                                                                    
Out[10]: 
7453  2H  1155    {'in': [{'playerId': ...
          2011    {'in': [{'playerId': ...
          2568    {'in': [{'playerId': ...
7454  2H  687     {'in': [{'playerId': ...
          1627    {'in': [{'playerId': ...
          2725    {'in': [{'playerId': ...
      1H  2833    {'in': [{'playerId': ...
dtype: object

现在您又拥有了一个 Series(带有 3d 索引),其中每个值都是一个字典,可以再次将其传递给Series构造函数。

In [11]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack().apply(pd.Series).stack()                                                                                                           
Out[11]: 
7453  2H  1155  in     [{'playerId': 281253}...
                out    [{'playerId': 449240}...
          2011  in       [{'playerId': 449089}]
                out       [{'playerId': 69374}]
          2568  in       [{'playerId': 481900}]
                out        [{'playerId': 1735}]
7454  2H  687   in          [{'playerId': 574}]
                out      [{'playerId': 578855}]
          1627  in       [{'playerId': 477400}]
                out       [{'playerId': 56386}]
          2725  in        [{'playerId': 56108}]
                out       [{'playerId': 56383}]
      1H  2833  in        [{'playerId': 56390}]
                out      [{'playerId': 208089}]
dtype: object

这是一种特殊情况,因为现在您的值不再是字典而是列表(每个都有一个元素)。 对于列表(不幸的是,不是字典),pandas 中的explode()方法可以为每个列表元素创建一个新行。

In [13]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack().apply(pd.Series).stack().explode()                                                                                                 
Out[13]: 
7453  2H  1155  in     {'playerId': 281253}
                in     {'playerId': 169212}
                out    {'playerId': 449240}
                out    {'playerId': 257943}
          2011  in     {'playerId': 449089}
                               ...         
7454  2H  1627  out     {'playerId': 56386}
          2725  in      {'playerId': 56108}
                out     {'playerId': 56383}
      1H  2833  in      {'playerId': 56390}
                out    {'playerId': 208089}
dtype: object

解压每个列表。 现在您再次拥有一个 Series(带有 4d 索引),其中每个值都是一个字典,可以再次将其传递给Series构造函数。

In [14]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack().apply(pd.Series).stack().explode().apply(pd.Series).stack()                                                                        
Out[14]: 
7453  2H  1155  in   playerId    281253
                     playerId    169212
                out  playerId    449240
                     playerId    257943
          2011  in   playerId    449089
                                  ...  
7454  2H  1627  out  playerId     56386
          2725  in   playerId     56108
                out  playerId     56383
      1H  2833  in   playerId     56390
                out  playerId    208089
dtype: int64

通过将Series构造函数应用于您的字典并重塑数据直到您可以再次应用它的这五次迭代,您的字典已完全解包。

为了匹配您想要的结果,您可以使用reset_index将所有级别的索引设置为列。

In [15]: pd.Series(d).apply(pd.Series).stack().apply(pd.Series).stack().apply(pd.Series).stack().explode().apply(pd.Series).stack().reset_index()                                                          
Out[15]: 
   level_0 level_1 level_2 level_3   level_4       0
0     7453      2H    1155      in  playerId  281253
1     7453      2H    1155      in  playerId  169212
2     7453      2H    1155     out  playerId  449240
3     7453      2H    1155     out  playerId  257943
4     7453      2H    2011      in  playerId  449089
..     ...     ...     ...     ...       ...     ...
11    7454      2H    1627     out  playerId   56386
12    7454      2H    2725      in  playerId   56108
13    7454      2H    2725     out  playerId   56383
14    7454      1H    2833      in  playerId   56390
15    7454      1H    2833     out  playerId  208089

系列和索引级别都没有名称。 默认情况下,它使用列号 ( 0 ) 作为值(应该是“playerId”), level_0level_4作为索引级别。 为了适当地设置这些,一种方法是在调用reset_index之前重命名Series ,然后使用rename重命名levels

我希望这会有所帮助

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM