[英]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。
更新:原則上,您可以將字典傳遞給DataFrame
和Series
構造函數
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_0
到level_4
作為索引級別。 為了適當地設置這些,一種方法是在調用reset_index
之前重命名Series
,然后使用rename
重命名levels
。
我希望這會有所幫助
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.