应优先使用 .xs() 提取 multiindex 单层值,如 df.xs('china', level='country');多条件筛选用 .query(),如 df.query("country=='us' and year>2020");层级切片需配合 pd.indexslice;操作后索引名丢失需手动恢复。

用 .xs() 提取单层索引值,别直接用方括号
直接写 df['A'] 或 df.loc['A'] 在 MultiIndex 上大概率报 KeyError: 'A',因为 Pandas 默认把第一级当行索引,但没说明你要切哪一级。这时候该用 .xs()(cross-section)——它专为从某一级提取固定值设计。
比如 DataFrame 的索引是 (country, year) 两级,想拿所有年份的 'China' 数据:
df.xs('China', level='country')
注意:level 参数可以是整数(0 表示第一级)或字符串(需索引有名字);不加 drop_level=False 时,默认会把被切掉的那一级从结果索引中移除。
- 如果保留原索引结构,加
drop_level=False -
.xs()只支持单值匹配,不能传列表;要多个值得换.query()或.isin() - 对列方向的 MultiIndex(即 columns 是 MultiIndex),同样适用
.xs(..., axis=1)
用 .query() 写可读性强的多级条件筛选
当要同时按多级索引做逻辑组合(比如 country == 'US' and year > 2020),.xs() 不够用,.loc 又容易写错层级顺序,.query() 是更稳的选择——它把索引名当变量用,语法接近 SQL。
前提是索引必须有名字(没名字就先 df.index.names = ['country', 'year']):
df.query("country in ['US', 'Japan'] and year >= 2021")
好处是支持 and/or/in/not in,还能混入列名(如 country == 'US' and sales > 1000)。
- 字符串值必须用单引号或双引号包裹,否则解析失败
-
.query()返回的是新 DataFrame,不修改原数据 - 性能上比纯布尔索引略慢,但开发效率和可维护性高得多
用 .loc 做精确层级定位时,必须用元组或 Slice
.loc 对 MultiIndex 的支持很“严格”:想切某一级的范围(比如所有 2020–2022 年),不能写 df.loc[:, 2020:2022](报 KeyError),得显式构造元组或用 pd.IndexSlice。
推荐用 pd.IndexSlice,避免手写大量 slice(None):
idx = pd.IndexSlice<br>df.loc[idx[:, 2020:2022], :]
上面这句意思是:第一级全取,第二级取 2020 到 2022(含端点)。
- 如果只切第一级,写
df.loc[idx['US', :], :];注意逗号后必须有:或空 slice - 省略
idx直接写元组(如df.loc[('US', 2021), :])只能取单行,不能带切片 - 层级顺序必须和
df.index的顺序一致,调换位置会报错
筛选后索引名丢失?记得检查 names 是否被重置
用 .xs()、.query() 或布尔索引后,有时发现索引名变成 None 或 None, None,尤其在链式操作(如 df.query(...).xs(...))之后。这不是 bug,是 Pandas 的默认行为:部分操作会丢弃索引名。
修复很简单,在关键步骤后手动恢复:
result = df.query("year > 2020").xs('US', level='country')<br>result.index.names = ['year'] # 显式设回
更稳妥的做法是在原始数据创建时就命名好索引(df.set_index(['country', 'year'], names=['country', 'year'])),并在中间步骤后留意 result.index.names 的输出。
这个细节容易被忽略,但一旦后续要用 .xs() 或 .query(),没有名字就会立刻报错或行为异常。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











