Construct pandas DataFrame из элементов во вложенном словаре
Предположим, что у меня есть вложенный словарь 'user_dict' со структурой:
Уровень 1: UserId (длинное целое число)
Уровень 2: Категория (строка)
Уровень 3: Ассортированные атрибуты (float, ints и т.д.)
Например, запись этого словаря будет следующей:
user_dict[12] = {
"Category 1": {"att_1": 1,
"att_2": "whatever"},
"Category 2": {"att_1": 23,
"att_2": "another"}}
каждый элемент в "user_dict" имеет ту же структуру, а "user_dict" содержит большое количество элементов, которые я хочу передать в pandas DataFrame, создавая серию из атрибутов. В этом случае для этой цели был бы полезен иерархический индекс.
В частности, мой вопрос заключается в том, существует ли способ помочь конструктору DataFrame понять, что серия должна быть построена из значений "уровня 3" в словаре?
Если я попробую что-то вроде:
df = pandas.DataFrame(users_summary)
Элементы в "уровне 1" (идентификаторы пользователя) берутся в виде столбцов, что противоположно тому, что я хочу достичь (иметь идентификатор пользователя как индекс).
Я знаю, что смогу построить серию после итерации над словарными записями, но если есть более прямой способ, это будет очень полезно. Аналогичным вопросом будет вопрос, можно ли построить pandas DataFrame из объектов json, перечисленных в файле.
Ответы
Ответ 1
A pandas 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
Ответ 2
Поэтому я использовал цикл for для итерации через словарь, но одна вещь, которую я обнаружил, работает намного быстрее, - это преобразовать в панель, а затем в кадр данных.
Скажем, у вас есть словарь d
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(), чтобы превратить ее в фреймворк данных. Я также использую reset_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
Наконец, если вам не нравится, как выглядит кадр, вы можете использовать функцию транспонирования панели для изменения внешнего вида перед вызовом to_frame() см. документацию здесь
http://pandas.pydata.org/pandas-docs/dev/generated/pandas.Panel.transpose.html
Как пример
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
Надеюсь, что это поможет.
Ответ 3
Если производительность важна, вы можете использовать словарное понимание, чтобы построить словарь, отображающий ключи в подкадры, а затем concat
их в конце:
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