从嵌套字典中的项目构建熊猫数据框架

假设我有一个带有结构的嵌套字典“ user _ dict”:

  • 等级1: UserId (长整数)
  • 第2级: 类别(字符串)
  • 等级3: 各种属性(浮点数、整数等)

例如,这本词典的一个条目是:

user_dict[12] = {
"Category 1": {"att_1": 1,
"att_2": "whatever"},
"Category 2": {"att_1": 23,
"att_2": "another"}}

user_dict中的每个条目都有相同的结构,而且 user_dict包含了大量的条目,我想把它们提供给一个熊猫数据框架(DataFrame) ,从属性构造这个系列。在这种情况下,分层索引将有助于实现这一目的。

具体来说,我的问题是是否存在一种方法来帮助 DataFrame 构造函数理解应该根据字典中“ level 3”的值来构建序列?

如果我试着这样做:

df = pandas.DataFrame(users_summary)

“ level 1”(UserId’s)中的项被视为列,这与我想要实现的目标相反(将 UserId’s 作为索引)。

我知道我可以在迭代字典条目之后构造这个系列,但是如果有更直接的方法,这将非常有用。一个类似的问题是,是否可以从文件中列出的 json 对象构造熊猫 DataFrame。

208772 次浏览

大熊猫 MultiIndex 由一系列元组组成。因此,最自然的方法是重新塑造您的输入字典,使其关键字是元组对应的多索引值,您需要。然后你可以使用 pd.DataFrame.from_dict构建你的数据帧,使用选项 orient='index':

user_dict = {12: {'Category 1': {'att_1': 1, 'att_2': 'whatever'},
'Category 2': {'att_1': 23, 'att_2': 'another'}},
15: {'Category 1': {'att_1': 10, 'att_2': 'foo'},
'Category 2': {'att_1': 30, 'att_2': 'bar'}}}


pd.DataFrame.from_dict({(i,j): user_dict[i][j]
for i in user_dict.keys()
for j in user_dict[i].keys()},
orient='index')




att_1     att_2
12 Category 1      1  whatever
Category 2     23   another
15 Category 1     10       foo
Category 2     30       bar

另一种方法是通过连接组件数据框架来构建数据框架:

user_ids = []
frames = []


for user_id, d in user_dict.iteritems():
user_ids.append(user_id)
frames.append(pd.DataFrame.from_dict(d, orient='index'))


pd.concat(frames, keys=user_ids)


att_1     att_2
12 Category 1      1  whatever
Category 2     23   another
15 Category 1     10       foo
Category 2     30       bar

所以我过去也使用 for 循环来迭代字典,但是我发现有一件事情可以更快地工作,那就是转换成一个面板,然后转换成一个数据框架。 假设你有一本字典

import pandas as pd
d
{'RAY Index': {datetime.date(2014, 11, 3): {'PX_LAST': 1199.46,
'PX_OPEN': 1200.14},
datetime.date(2014, 11, 4): {'PX_LAST': 1195.323, 'PX_OPEN': 1197.69},
datetime.date(2014, 11, 5): {'PX_LAST': 1200.936, 'PX_OPEN': 1195.32},
datetime.date(2014, 11, 6): {'PX_LAST': 1206.061, 'PX_OPEN': 1200.62}},
'SPX Index': {datetime.date(2014, 11, 3): {'PX_LAST': 2017.81,
'PX_OPEN': 2018.21},
datetime.date(2014, 11, 4): {'PX_LAST': 2012.1, 'PX_OPEN': 2015.81},
datetime.date(2014, 11, 5): {'PX_LAST': 2023.57, 'PX_OPEN': 2015.29},
datetime.date(2014, 11, 6): {'PX_LAST': 2031.21, 'PX_OPEN': 2023.33}}}

命令

pd.Panel(d)
<class 'pandas.core.panel.Panel'>
Dimensions: 2 (items) x 2 (major_axis) x 4 (minor_axis)
Items axis: RAY Index to SPX Index
Major_axis axis: PX_LAST to PX_OPEN
Minor_axis axis: 2014-11-03 to 2014-11-06

其中 pd.Panel (d)[ item ]生成一个数据框架

pd.Panel(d)['SPX Index']
2014-11-03  2014-11-04  2014-11-05 2014-11-06
PX_LAST 2017.81 2012.10 2023.57 2031.21
PX_OPEN 2018.21 2015.81 2015.29 2023.33

然后可以点击命令 to _ frame ()将其转换为数据帧。我还使用 set _ index 将主轴和次轴转换为列,而不是将它们作为索引。

pd.Panel(d).to_frame().reset_index()
major   minor      RAY Index    SPX Index
PX_LAST 2014-11-03  1199.460    2017.81
PX_LAST 2014-11-04  1195.323    2012.10
PX_LAST 2014-11-05  1200.936    2023.57
PX_LAST 2014-11-06  1206.061    2031.21
PX_OPEN 2014-11-03  1200.140    2018.21
PX_OPEN 2014-11-04  1197.690    2015.81
PX_OPEN 2014-11-05  1195.320    2015.29
PX_OPEN 2014-11-06  1200.620    2023.33

最后,如果您不喜欢帧的外观,可以使用 panel 的转置函数在调用 to _ frame ()之前更改外观,请参阅此处的文档 Http://pandas.pydata.org/pandas-docs/dev/generated/pandas

举个例子

pd.Panel(d).transpose(2,0,1).to_frame().reset_index()
major        minor  2014-11-03  2014-11-04  2014-11-05  2014-11-06
RAY Index   PX_LAST 1199.46    1195.323     1200.936    1206.061
RAY Index   PX_OPEN 1200.14    1197.690     1195.320    1200.620
SPX Index   PX_LAST 2017.81    2012.100     2023.570    2031.210
SPX Index   PX_OPEN 2018.21    2015.810     2015.290    2023.330

希望这个能帮上忙。

接受字典。考虑到这一点,通过使用 字典理解构建字典映射键到子帧,可以在简单性和性能方面改进当前接受的答案。

pd.concat({k: pd.DataFrame(v).T for k, v in user_dict.items()}, axis=0)

或者,

pd.concat({
k: pd.DataFrame.from_dict(v, 'index') for k, v in user_dict.items()
},
axis=0)

              att_1     att_2
12 Category 1     1  whatever
Category 2    23   another
15 Category 1    10       foo
Category 2    30       bar

如果有人想要获得没有多重索引的“长格式”(叶值具有相同类型)数据帧,您可以这样做:

pd.DataFrame.from_records(
[
(level1, level2, level3, leaf)
for level1, level2_dict in user_dict.items()
for level2, level3_dict in level2_dict.items()
for level3, leaf in level3_dict.items()
],
columns=['UserId', 'Category', 'Attribute', 'value']
)


UserId    Category Attribute     value
0       12  Category 1     att_1         1
1       12  Category 1     att_2  whatever
2       12  Category 2     att_1        23
3       12  Category 2     att_2   another
4       15  Category 1     att_1        10
5       15  Category 1     att_2       foo
6       15  Category 2     att_1        30
7       15  Category 2     att_2       bar

(我知道最初的问题可能希望(I)将级别1和2作为多索引,级别3作为列和(II)。)询问其他方式而不是迭代结果中的值。但是我希望这个答案仍然是相关的和有用的(一) : 对于像我这样的人,他们试图找到一种方法把嵌套的字典变成这个形状,谷歌只返回这个问题和(二): 因为其他的答案也涉及到一些迭代,而且我发现这种方法很灵活,很容易阅读; 但是对性能不确定。)

基于已证实的答案,对我来说这个方法最有效:

ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0)
ab.T

对于其他表示数据的方法,您不需要做太多。例如,如果只希望“外部”键是一个索引,“内部”键是列,值是单元格值,这样就可以实现:

df = pd.DataFrame.from_dict(user_dict, orient='index')




这个解决方案应该通过平坦字典键到元组链来适用于任意深度

def flatten_dict(nested_dict):
res = {}
if isinstance(nested_dict, dict):
for k in nested_dict:
flattened_dict = flatten_dict(nested_dict[k])
for key, val in flattened_dict.items():
key = list(key)
key.insert(0, k)
res[tuple(key)] = val
else:
res[()] = nested_dict
return res




def nested_dict_to_df(values_dict):
flat_dict = flatten_dict(values_dict)
df = pd.DataFrame.from_dict(flat_dict, orient="index")
df.index = pd.MultiIndex.from_tuples(df.index)
df = df.unstack(level=-1)
df.columns = df.columns.map("{0[1]}".format)
return df