Pandas MultiIndex(超过 2 个级别)DataFrame 到嵌套 Dict/JSON
Posted
技术标签:
【中文标题】Pandas MultiIndex(超过 2 个级别)DataFrame 到嵌套 Dict/JSON【英文标题】:Pandas MultiIndex (more than 2 levels) DataFrame to Nested Dict/JSON 【发布时间】:2018-11-28 11:57:05 【问题描述】:这个问题类似于this one,但我想更进一步。是否可以扩展解决方案以使用更多级别?多级数据框的.to_dict()
方法有一些有前途的选项,但它们中的大多数将返回由元组(即(A, 0, 0): 274.0
)索引的条目,而不是将它们嵌套在字典中。
作为我想要完成的示例,请考虑这个多索引数据框:
data = 0:
('A', 0, 0): 274.0,
('A', 0, 1): 19.0,
('A', 1, 0): 67.0,
('A', 1, 1): 12.0,
('B', 0, 0): 83.0,
('B', 0, 1): 45.0
,
1:
('A', 0, 0): 254.0,
('A', 0, 1): 11.0,
('A', 1, 0): 58.0,
('A', 1, 1): 11.0,
('B', 0, 0): 76.0,
('B', 0, 1): 56.0
df = pd.DataFrame(data).T
df.index = ['entry1', 'entry2']
df
# output:
A B
0 1 0
0 1 0 1 0 1
entry1 274.0 19.0 67.0 12.0 83.0 45.0
entry2 254.0 11.0 58.0 11.0 76.0 56.0
您可以想象我们这里有很多记录,而不仅仅是两条,而且索引名称可能是更长的字符串。你怎么能把它变成像这样的嵌套字典(或直接变成 JSON):
[
'entry1': 'A': 0: 0: 274.0, 1: 19.0, 1: 0: 67.0, 1: 12.0,
'B': 0: 0: 83.0, 1: 45.0,
'entry2': 'A': 0: 0: 254.0, 1: 11.0, 1: 0: 58.0, 1: 11.0,
'B': 0: 0: 76.0, 1: 56.0
]
我认为一些递归可能会有所帮助,可能类似于 this,但到目前为止还没有成功。
【问题讨论】:
【参考方案1】:所以,你真的需要在这里做两件事:
df.to_dict()
将此转换为嵌套字典。
df.to_dict(orient='index')
给你一个以索引为键的字典;它看起来像这样:
>>> df.to_dict(orient='index')
'entry1': ('A', 0, 0): 274.0,
('A', 0, 1): 19.0,
('A', 1, 0): 67.0,
('A', 1, 1): 12.0,
('B', 0, 0): 83.0,
('B', 0, 1): 45.0,
'entry2': ('A', 0, 0): 254.0,
('A', 0, 1): 11.0,
('A', 1, 0): 58.0,
('A', 1, 1): 11.0,
('B', 0, 0): 76.0,
('B', 0, 1): 56.0
现在你需要嵌套它。这是一个技巧from Martijn Pieters 做到这一点:
def nest(d: dict) -> dict:
result =
for key, value in d.items():
target = result
for k in key[:-1]: # traverse all keys but the last
target = target.setdefault(k, )
target[key[-1]] = value
return result
把这一切放在一起:
def df_to_nested_dict(df: pd.DataFrame) -> dict:
d = df.to_dict(orient='index')
return k: nest(v) for k, v in d.items()
输出:
>>> df_to_nested_dict(df)
'entry1': 'A': 0: 0: 274.0, 1: 19.0, 1: 0: 67.0, 1: 12.0,
'B': 0: 0: 83.0, 1: 45.0,
'entry2': 'A': 0: 0: 254.0, 1: 11.0, 1: 0: 58.0, 1: 11.0,
'B': 0: 0: 76.0, 1: 56.0
【讨论】:
使用类型提示你会得到 +1 :) 很好,很干净的答案!不幸的是,Pandas 没有一种不涉及手动迭代每一行的方法【参考方案2】:我从上一个答案中汲取了想法并稍微修改了它。
1) 从*** 获取函数nested_dict,创建字典
from collections import defaultdict
def nested_dict(n, type):
if n == 1:
return defaultdict(type)
else:
return defaultdict(lambda: nested_dict(n-1, type))
2 写了如下函数:
def df_to_nested_dict(self, df, type): # Get the number of levels temp = df.index.names lvl = len(temp) # Create the target dictionary new_nested_dict=nested_dict(lvl, type) # Convert the dataframe to a dictionary temp_dict = df.to_dict(orient='index') for x, y in temp_dict.items(): dict_keys = '' # Process the individual items from the key for item in x: dkey = '[%d]' % item dict_keys = dict_keys + dkey # Create a string and execute it dict_update = 'new_nested_dict%s = y' % dict_keys exec(dict_update) return new_nested_dict
思路相同,但做起来略有不同
【讨论】:
以上是关于Pandas MultiIndex(超过 2 个级别)DataFrame 到嵌套 Dict/JSON的主要内容,如果未能解决你的问题,请参考以下文章
在 Pandas 中将两个 MultiIndex 级别合并为一个
Pandas 通过 Tuple 重命名 MultiIndex 的单行