pandas按指定顺序访问MultiIndex列时出现列结构异常问题
Pandas MultiIndex列筛选后层级结构错乱问题
问题复现
首先构建带MultiIndex列的测试DataFrame:
import pandas as pd import numpy as np df = pd.DataFrame(np.random.randint(0,1000, (5,6)), \ columns = pd.MultiIndex.from_product([['CPC', 'Conversions'], ['April', 'June', 'May']])).rename_axis(index = {None : 'idx'}) df
DataFrame原始输出如下:
CPC Conversions April June May April June May idx 0 663 964 971 663 76 927 1 405 217 754 370 306 34 2 474 229 664 354 66 885 3 73 538 139 417 876 855 4 619 618 618 455 134 805
需求是按April、May、June的正确月份顺序排列二级列,执行如下筛选代码:
df.loc[:, (slice(None), ['April', 'May', 'June'])]
返回结果列排布异常,同月份下的CPC、Conversions列被拆分,未保留原有的第一层级分组结构,输出如下:
CPC Conversions CPC Conversions CPC Conversions April April May May June June idx 0 806 202 963 975 110 55 1 263 884 442 563 216 694 2 462 361 780 412 858 670 3 742 756 525 33 477 826 4 579 332 91 802 829 231
但如果仅选择2个月份执行相同逻辑,返回的列结构完全正常:
df.loc[:, (slice(None), ['April', 'May'])]
输出:
CPC Conversions April May April May idx 0 856 619 180 593 1 64 403 929 80 2 973 285 803 967 3 769 405 701 267 4 940 368 863 717
问题成因
这不是pandas的功能bug,是多级索引使用列表传入筛选条件时的固有匹配逻辑:当传入的二级筛选列表长度≥3、且顺序和原MultiIndex中二级值的存储顺序不一致时,pandas会直接按传入的列表值顺序逐一生成(一级索引值, 二级索引值)的索引对做匹配,不会优先保留原有一级索引的分组结构;而传入长度为2的列表时,会触发pandas内置的短列表匹配优化,自动保留原有层级顺序,才会出现选2个月正常、选3个月结构错乱的反差。
通用解决方法
不需要手动硬编码重建整个MultiIndex,以下两种方案都可以自动适配后续列层级的变动:
- 方案1:筛选后按索引层级自定义排序
先完成筛选,再给二级月份设置自定义有序类别,按一级索引、二级索引的优先级重排列即可:# 执行筛选 res = df.loc[:, (slice(None), ['April', 'May', 'June'])] # 定义月份的正确顺序 month_sort = pd.CategoricalDtype(categories=['April', 'May', 'June'], ordered=True) # 给二级列设置有序类别 res.columns = res.columns.set_levels( [res.columns.levels[0], res.columns.levels[1].astype(month_sort)], level=[0, 1] ) # 按层级优先级重排列 res = res.sort_index(axis=1, level=[0, 1]) - 方案2:用目标多级索引做列重索引
直接基于原有列的一级索引值,和需要的二级顺序生成目标列结构,用reindex对齐即可,后续一级列新增、删除字段都不需要修改代码:# 生成保留原有一级分组、按指定顺序排列二级值的目标列 target_cols = pd.MultiIndex.from_product([df.columns.levels[0], ['April', 'May', 'June']]) res = df.reindex(columns=target_cols)
两种方案返回的结果都会保留原有的「一级指标分组在前,二级月份按自定义顺序排列」的结构,不需要硬编码全量列名,适配性更强。
内容的提问来源于stack exchange,提问作者Dhruv
相关产品推荐
相关产品推荐

