Flatten DataFrame with multi-index columns

It seems to me that melt (aka unpivot) is very close to what you want to do:

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

There's a rogue column (stock), that appears here that column header is constant in piv. If we drop it first the melt works 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

Edit: The above actually drops the index, you need to make it a column with 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

I know that the question has already been answered, but for my dataset multiindex column problem, the provided solution was unefficient. So here I am posting another solution for unpivoting multiindex columns using pandas.

Here is the problem I had:

enter image description here

As one can see, the dataframe is composed of 3 multiindex, and two levels of multiindex columns.

The desired dataframe format was:

enter image description here

When I tried the options given above, the pd.melt function didn't allow to have more than one column in the var_name attribute. Therefore, every time that I tried a melt, I would end up losing some attribute from my table.

The solution I found was to apply a double stacking function over my dataframe.

Before the coding, it is worth notice that the desired var_name for my unpivoted table column was "Populacao residente em domicilios particulares ocupados" (see in the code below). Therefore, for all my value entries, they should be stacked in this newly created var_name new column.

Here is a snippet code:

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()

Another solution I found was to first apply a stacking function over the dataframe and then apply the melt.

Here is an alternative code:

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()

Sincerely yours,

Philipe Riskalla Leal


>>> 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

then all you need is to change last column name from 0 to stock.