Подтвердить что ты не робот

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, перечисленных в файле.

4b9b3361

Ответ 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