如何在对pandas DataFrame进行切片后更新MultiIndex的级别?

23

我有一个带有pandas MultiIndex的Dataframe:

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

但是当我查询MultiIndex时,它仍然具有两个国家的级别。

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

但这看起来相当混乱。我是否漏掉了更好的方法?

3个回答

21

从版本pandas 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'])

14

这是我以前遇到过的问题。删除列或行不会改变底层的MultiIndex,出于性能和哲学上的考虑,这在官方上并不被认为是一个bug(在此查看更多)。简短的回答是开发者说“这不是MultiIndex的作用”。如果你需要修改后MultiIndex级别内容的列表,例如用于迭代或检查是否包含某些内容,可以使用:

df.index.get_level_values(<levelname>)

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

所以我想这里的“技巧”是,使用 get_level_values 而不是仅仅使用 .index 或者 .columns 是API的本机方式来实现这一点。


哦,如果你不想要重复的话,可以在那里加上 .unique()。默认的级别值包括每个出现的值,所以在典型的多索引场景中会看到很多重复。 - Ezekiel Kruglick
你也可以使用 unique(data.index.values) 来获取所有级别的值。 - user2699

0

如果没有比你现在所做的重建索引(或类似方式)更好的消除未使用国家的“内置”方法,那我会感到惊讶。如果你在切片之前和之后查看索引:

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”仍然是有效选择的事实。要从枚举中删除“CAN”,你必须更改列定义;这相当于在pandas中重新索引数据帧。


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