Flatten DataFrame с многоиндексными столбцами
Я бы хотел конвертировать Pandas DataFrame, который был получен из сводной таблицы в представление строки, как показано ниже.
Вот где я нахожусь:
import pandas as pd
import numpy as np
df = pd.DataFrame({
'goods': ['a', 'a', 'b', 'b', 'b'],
'stock': [5, 10, 30, 40, 10],
'category': ['c1', 'c2', 'c1', 'c2', 'c1'],
'date': pd.to_datetime(['2014-01-01', '2014-02-01', '2014-01-06', '2014-02-09', '2014-03-09'])
})
# we don't care about year in this example
df['month'] = df['date'].map(lambda x: x.month)
piv = df.pivot_table(["stock"], "month", ["goods", "category"], aggfunc="sum")
piv = piv.reindex(np.arange(piv.index[0], piv.index[-1] + 1))
piv = piv.ffill(axis=0)
piv = piv.fillna(0)
print piv
что приводит к
stock
goods a b
category c1 c2 c1 c2
month
1 5 0 30 0
2 5 10 30 40
3 5 10 10 40
И здесь я хочу добраться.
goods category month stock
a c1 1 5
a c1 2 0
a c1 3 0
a c2 1 0
a c2 2 10
a c2 3 0
b c1 1 30
b c1 2 0
b c1 3 10
b c2 1 0
b c2 2 40
b c2 3 0
Ранее, я использовал
piv = piv.stack()
piv = piv.reset_index()
print piv
чтобы избавиться от мультииндекса, но это приводит к этому, потому что я теперь поворачиваюсь на двух столбцах (["goods", "category"]
):
month category stock
goods a b
0 1 c1 5 30
1 1 c2 0 0
2 2 c1 5 30
3 2 c2 10 40
4 3 c1 5 10
5 3 c2 10 40
Кто-нибудь знает, как я могу избавиться от мультииндекса в столбце и получить результат в DataFrame примерного формата?
Ответы
Ответ 1
>>> piv.unstack().reset_index().drop('level_0', axis=1)
goods category month 0
0 a c1 1 5
1 a c1 2 5
2 a c1 3 5
3 a c2 1 0
4 a c2 2 10
5 a c2 3 10
6 b c1 1 30
7 b c1 2 30
8 b c1 3 10
9 b c2 1 0
10 b c2 2 40
11 b c2 3 40
тогда вам нужно только изменить имя последнего столбца от 0
до stock
.
Ответ 2
Мне кажется, что melt
(aka univot) очень близок к тому, что вы хотите сделать:
In [11]: pd.melt(piv)
Out[11]:
NaN goods category value
0 stock a c1 5
1 stock a c1 5
2 stock a c1 5
3 stock a c2 0
4 stock a c2 10
5 stock a c2 10
6 stock b c1 30
7 stock b c1 30
8 stock b c1 10
9 stock b c2 0
10 stock b c2 40
11 stock b c2 40
Там находится столбец-жулик (запас), который появляется здесь, что заголовок столбца является постоянным в piv. Если мы сначала откачем, расплав работает OOTB:
In [12]: piv.columns = piv.columns.droplevel(0)
In [13]: pd.melt(piv)
Out[13]:
goods category value
0 a c1 5
1 a c1 5
2 a c1 5
3 a c2 0
4 a c2 10
5 a c2 10
6 b c1 30
7 b c1 30
8 b c1 10
9 b c2 0
10 b c2 40
11 b c2 40
Изменить: вышеописанное фактически снижает индекс, вам нужно сделать столбец с reset_index
:
In [21]: pd.melt(piv.reset_index(), id_vars=['month'], value_name='stock')
Out[21]:
month goods category stock
0 1 a c1 5
1 2 a c1 5
2 3 a c1 5
3 1 a c2 0
4 2 a c2 10
5 3 a c2 10
6 1 b c1 30
7 2 b c1 30
8 3 b c1 10
9 1 b c2 0
10 2 b c2 40
11 3 b c2 40
Ответ 3
Я знаю, что на этот вопрос уже был дан ответ, но для моей проблемы многоиндексных столбцов набора данных предоставленное решение было неэффективным. Поэтому здесь я выкладываю другое решение для разворачивания многоиндексных столбцов с помощью панд.
Вот проблема, которая у меня была:
![enter image description here]()
Как видно, фрейм данных состоит из 3 многоиндексных и двухуровневых многоиндексных столбцов.
Желаемый формат данных:
![enter image description here]()
Когда я попробовал параметры, указанные выше, функция pd.melt не позволяла иметь более одного столбца в атрибуте var_name. Поэтому каждый раз, когда я пытался расплавиться, я терял какой-то атрибут со своего стола.
Решение, которое я нашел, состояло в том, чтобы применить функцию двойного стека к моему фрейму данных.
Перед кодированием стоит заметить, что желаемое имя var_name для моего столбца непивотированной таблицы было "Populacao residente em domicilios speculares ocupados" (см. Код ниже). Поэтому для всех моих записей значений они должны быть сложены во вновь созданном новом столбце var_name.
Вот фрагмент кода:
import pandas as pd
# reading my table
df = pd.read_excel(r'my_table.xls', sep=',', header=[2,3], encoding='latin3',
index_col=[0,1,2], na_values=['-', ' ', '*'], squeeze=True).fillna(0)
df.index.names = ['COD_MUNIC_7', 'NOME_MUN', 'TIPO']
df.columns.names = ['sexo', 'faixa_etaria']
df.head()
# making the stacking:
df = pd.DataFrame(pd.Series(df.stack(level=0).stack(), name='Populacao residente em domicilios particulares ocupados')).reset_index()
df.head()
Другое решение, которое я нашел, состояло в том, чтобы сначала применить функцию стекирования к фрейму данных, а затем применить расплав.
Вот альтернативный код:
df = df.stack('faixa_etaria').reset_index().melt(id_vars=['COD_MUNIC_7', 'NOME_MUN','TIPO', 'faixa_etaria'],
value_vars=['Homens', 'Mulheres'],
value_name='Populacao residente em domicilios particulares ocupados',
var_name='sexo')
df.head()
Искренне Ваш,
Филипп Рискалла Лил