Предположим, у меня есть вложенный словарь 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
содержит большое количество элементов, которые я хочу передать в фрейм данных pandas, построив серию из атрибутов. В этом случае для этой цели будет полезен иерархический индекс.
В частности, мой вопрос заключается в том, существует ли способ помочь конструктору DataFrame понять, что ряды должны быть построены из значений «уровня 3» в словаре?
Если я попробую что-то вроде:
df = pandas.DataFrame(users_summary)
Элементы на «уровне 1» (UserId) принимаются как столбцы, что противоположно тому, чего я хочу достичь (иметь UserId в качестве индекса).
Я знаю, что могу построить серию после перебора словарных статей, но если есть более прямой способ, это было бы очень полезно. Аналогичный вопрос будет спрашивать, можно ли построить pandas DataFrame из объектов json, перечисленных в файле.
источник
Ответы:
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
источник
pd.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
источник
12:{cat1:{cat11:{att1:val1,att2:val2}}}
. Другими словами: как можно обобщить решение на несущественное количество категорий?json_normalize
. У меня есть другой ответ, который показывает, как это работает.v
это одно целое число. Вы знаете альтернативу в таком случае?Итак, я использовал цикл 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
Надеюсь это поможет.
источник
Если кто-то хочет получить фрейм данных в «длинном формате» (конечные значения имеют один и тот же тип) без мультииндекса, вы можете сделать это:
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.) Спрашивает о других способах, кроме итерации значений в dict. Но я надеюсь, что этот ответ все еще актуален и полезно (I.): людям вроде меня, которые пытались найти способ получить вложенный dict в эту форму, и Google возвращает только этот вопрос и (II.): потому что другие ответы также включают некоторую итерацию, и я нахожу это подход гибкий и легкий для чтения; однако не уверен в производительности.)
источник
Основываясь на проверенном ответе, для меня это сработало лучше всего:
ab = pd.concat({k: pd.DataFrame(v).T for k, v in data.items()}, axis=0) ab.T
источник