使用多级索引列展开DataFrame

15
我希望将从数据透视表中导出的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吗?
3个回答

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


1
哦,糟糕,我的答案丢失了索引中的月份 :( - Andy Hayden
谢谢。它在我的示例中正常工作。但是,我不太明白为什么之前使用stack可以,而现在应该使用unstack - orange

5

我认为melt(也称为反透视)非常接近你想要做的事情:

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中该列头是恒定的。如果我们首先删除它,那么melt就可以正常工作:

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

有趣的函数,但“月份”列去哪了? - orange
@orange,索引似乎已经被删除了,我将保留这个答案,因为它可能对某些人有用(我也希望我能修复它!)。 - Andy Hayden
@orange 我不会说这是一个错误,但可能是一个功能/增强(不要删除索引)。此外,您无法传递诸如 col_level=[1, 2] 这样的内容,在这里将非常有用。 - Andy Hayden
@orange明白了吗(我想是的)?一个简单的reset_index就足够了 :) - Andy Hayden
太好了 - 非常感谢。我会尝试这个方法,因为现在我必须根据分组元素的数量选择stackunstack。使用melt将更加简化。抱歉,我只能接受一个答案(已经点赞了你的答案)。 - orange
显示剩余2条评论

1

我知道这个问题已经有答案了,但是对于我的数据集多重索引列的问题,提供的解决方案效率不高。因此,我在这里发布另一种使用pandas展开多重索引列的解决方案。

这是我遇到的问题:

enter image description here

作为一个人可以看到,数据框由3个多索引和两个多索引列级别组成。
期望的数据框格式是:

enter image description here

当我尝试上述给出的选项时,pd.melt函数不允许在var_name属性中有多列。因此,每次我尝试melt时,都会丢失我的表中的某些属性。
我找到的解决方案是对我的数据框应用双重堆叠函数。
在编码之前,值得注意的是,我未枚举表格列的所需var_name为“Populacao residente em domicilios particulares 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()

真诚地致意,

Philipe Riskalla Leal


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接