如何在切分DataFrame后更新一张pandas MultiIndex的水平?

我有一个pandasMultiIndex数据框:

In [1]: import pandas as pd In [2]: multi_index = pd.MultiIndex.from_product([['CAN','USA'],['total']],names=['country','sex']) In [3]: df = pd.DataFrame({'pop':[35,318]},index=multi_index) In [4]: df Out[4]: pop country sex CAN total 35 USA total 318 

然后我从DataFrame中删除一些行:

 In [5]: df = df.query('pop > 100') In [6]: df Out[6]: pop country sex USA total 318 

但是当我咨询MutliIndex时,它仍然有两个国家的水平。

 In [7]: df.index.levels[0] Out[7]: Index([u'CAN', u'USA'], dtype='object') 

我可以用一种相当奇怪的方式来解决这个问题:

 In [8]: idx_names = df.index.names In [9]: df = df.reset_index(drop=False) In [10]: df = df.set_index(idx_names) In [11]: df Out[11]: pop country sex USA total 318 In [12]: df.index.levels[0] Out[12]: Index([u'USA'], dtype='object') 

但是这似乎相当混乱。 有没有更好的方法我错过了?

这是之前咬过我的东西。 由于性能和哲学方面的原因,删除列或行并不会改变底层的MultiIndex,而这正式不被视为一个错误( 在这里阅读更多 )。 简单的答案是开发者说“这不是MultiIndex的目的”。 如果在修改后需要MultiIndex级别的内容列表(例如迭代或检查是否包含某些内容),则可以使用:

 df.index.get_level_values(<levelname>) 

这将返回该索引级别内的当前活动值。

所以我猜这里的“诀窍”是使用API​​本地方法来使用get_level_values而不是.index或.columns

从版本0.20.0开始,使用MultiIndex.remove_unused_levels

 print (df.index) MultiIndex(levels=[['CAN', 'USA'], ['total']], labels=[[1], [0]], names=['country', 'sex']) df.index = df.index.remove_unused_levels() print (df.index) MultiIndex(levels=[['USA'], ['total']], labels=[[0], [0]], names=['country', 'sex']) 

如果有一种更“内置”的方式来消除未使用的国家,而不是像你所做的那样(或类似的方式)重新创build索引,我会感到惊讶。 如果你看看你的索引在切片前后:

 In [165]: df.index Out[165]: MultiIndex(levels=[[u'CAN', u'USA'], [u'total']], labels=[[0, 1], [0, 0]], names=[u'country', u'sex']) In [166]: df = df.query('pop > 100') In [167]: df.index Out[167]: MultiIndex(levels=[[u'CAN', u'USA'], [u'total']], labels=[[1], [0]], names=[u'country', u'sex']) 

您可以看到标签(这些标签是级别值的索引)已经更新,但没有更新级别值。 这可能是一个不完美的类比,但是它让我觉得这个级别值与数据库表中的枚举列类似,而标签类似于表中行的实际值。 如果删除表中的所有行的值为“CAN”,则不会改变“CAN”仍然是基于列定义的有效select的事实。 要从枚举中删除“CAN”,您必须更改列定义; 这相当于将dataframe重新编入pandas。