问题 在切片DataFrame后如何更新pandas MultiIndex的级别?


我有一个带有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

但是当我咨询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')

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


9426
2018-02-27 19:08


起源



答案:


这是以前咬过我的东西。出于性能和哲学原因,删除列或行不会更改基础MultiIndex,这正式不被视为错误(在这里阅读更多)。简短的回答是开发人员说“这不是MultiIndex的用途”。如果您需要修改后的MultiIndex级别的内容列表,例如迭代或检查是否包含某些内容,您可以使用:

df.index.get_level_values(<levelname>)

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

所以我想这里的“技巧”是API本机方式是使用get_level_values而不仅仅是.index或.columns


9
2018-05-16 17:31



哦,你可以添加.unique(),如果你不想重复。默认级别值包括每次出现,因此您将在典型的多索引方案中看到大量重复项 - Ezekiel Kruglick
你也可以使用 unique(data.index.values) 获取所有级别的值。 - user2699


答案:


这是以前咬过我的东西。出于性能和哲学原因,删除列或行不会更改基础MultiIndex,这正式不被视为错误(在这里阅读更多)。简短的回答是开发人员说“这不是MultiIndex的用途”。如果您需要修改后的MultiIndex级别的内容列表,例如迭代或检查是否包含某些内容,您可以使用:

df.index.get_level_values(<levelname>)

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

所以我想这里的“技巧”是API本机方式是使用get_level_values而不仅仅是.index或.columns


9
2018-05-16 17:31



哦,你可以添加.unique(),如果你不想重复。默认级别值包括每次出现,因此您将在典型的多索引方案中看到大量重复项 - Ezekiel Kruglick
你也可以使用 unique(data.index.values) 获取所有级别的值。 - user2699


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

7
2018-05-06 18:59





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

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中的数据帧。


0
2018-02-27 20:06