如何在切片 DataFrame 后更新 pandas MultiIndex 的级别?
How do you update the levels of a pandas MultiIndex after slicing its DataFrame?
我有一个带有 pandas MultiIndex 的数据框:
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')
但这看起来很乱。有没有更好的方法我想念?
如果有更多 "built-in" 的方法来消除未使用的国家,而不是按照您正在做的方式(或某种类似的方式)重新创建索引,我会感到惊讶。如果您查看切片前后的索引:
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'])
您可以看到标签(级别值的索引)已更新,但级别值未更新。这可能是一个不完美的类比,但令我印象深刻的是,级别值类似于数据库 table 中的枚举列,而标签类似于 table 中行的实际值。如果您删除 table 中值为 "CAN" 的所有行,这不会改变 "CAN" 仍然是基于列定义的有效选择这一事实。要从枚举中删除 "CAN" ,您必须更改列定义;这相当于在 pandas.
中重新索引数据框
这是以前困扰我的事情。出于性能和哲学原因,删除列或行不会更改底层 MultiIndex,并且这在官方上不被视为错误 (read more here)。简短的回答是开发人员说 "that's not what the MultiIndex is for"。如果您需要修改后的 MultiIndex 级别的内容列表,例如用于迭代或检查是否包含某些内容,您可以使用:
df.index.get_level_values(<levelname>)
这 returns 该指数水平内的当前活跃值。
所以我猜 "trick" 这里的 API 原生方法是使用 get_level_values 而不是 .index 或 .columns
来自版本 pandas 0.20.0+
use 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'])
我有一个带有 pandas MultiIndex 的数据框:
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')
但这看起来很乱。有没有更好的方法我想念?
如果有更多 "built-in" 的方法来消除未使用的国家,而不是按照您正在做的方式(或某种类似的方式)重新创建索引,我会感到惊讶。如果您查看切片前后的索引:
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'])
您可以看到标签(级别值的索引)已更新,但级别值未更新。这可能是一个不完美的类比,但令我印象深刻的是,级别值类似于数据库 table 中的枚举列,而标签类似于 table 中行的实际值。如果您删除 table 中值为 "CAN" 的所有行,这不会改变 "CAN" 仍然是基于列定义的有效选择这一事实。要从枚举中删除 "CAN" ,您必须更改列定义;这相当于在 pandas.
中重新索引数据框这是以前困扰我的事情。出于性能和哲学原因,删除列或行不会更改底层 MultiIndex,并且这在官方上不被视为错误 (read more here)。简短的回答是开发人员说 "that's not what the MultiIndex is for"。如果您需要修改后的 MultiIndex 级别的内容列表,例如用于迭代或检查是否包含某些内容,您可以使用:
df.index.get_level_values(<levelname>)
这 returns 该指数水平内的当前活跃值。
所以我猜 "trick" 这里的 API 原生方法是使用 get_level_values 而不是 .index 或 .columns
来自版本 pandas 0.20.0+
use 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'])