多级表头是列名由元组构成的MultiIndex结构,如('sales', '2023'),故不能用columns[0]直接取第一列名;因其索引对象为MultiIndex而非普通列表,需用df.columns.get_level_values(0)等方法访问特定层级。

什么是多级表头,为什么不能直接用 columns[0] 取第一列名?
多级表头(MultiIndex columns)常见于 pivot_table、groupby.agg 或读取 Excel 多行表头时。它的列名不是字符串,而是元组,比如 ('sales', '2023') 或 ('price', 'mean')。直接写 df['sales'] 会报 KeyError,因为实际列名是元组,不是字符串。
展平本质是把元组列名转成唯一字符串,关键在于处理层级冲突和重复命名。
- 默认用下划线拼接:
'_'.join(col).strip(),但若某层为空(如(None, 'A')),会生成'_A',难读 - 用
map(str)再 join 容易把None变成'None',污染语义 - 推荐先过滤掉
None:'_'.join([str(c) for c in col if c is not None])
用 map() + 自定义函数展平最灵活
直接改 df.columns 是最常用且可控的方式。别用 df.columns.tolist() 再赋值——那会丢失 MultiIndex 的结构信息,导致后续 xs 或 swaplevel 失效。
实操建议:
立即学习“Python免费学习笔记(深入)”;
图片提示词生成器?不止如此。 马甲系统 —— 把脑海中的画面,翻译成AI能理解的专业表达。 用得越多,它越懂你:首次需要多问几句确认方向,用久了几乎一说就懂。 用得越多,它越快:缓存机制让后续对话越来越省。 RAG进化:成功案例持续入库,越跑越聪明。 输入「新手指南」查看完整功能介绍
- 写一个清洗函数,保留业务含义:例如把
('product', 'name')→'product_name',而(None, 'count')→'count' - 避免硬编码层级数:用
len(col)判断,兼容两层或三层表头 - 遇到重复新列名必须去重:加序号后缀,否则
df.to_csv()会静默丢列
示例:
def flatten_cols(col):
parts = [str(c) for c in col if c is not None]
return '_'.join(parts) if parts else 'unnamed'
df.columns = df.columns.map(flatten_cols)
reset_index() 不是展平列,别混淆
新手常误以为 df.reset_index() 能处理列名——它只作用于行索引(index),对 columns 完全没影响。如果 DataFrame 还有行 MultiIndex,调用它会把索引列“塞进”数据里,反而让结构更乱。
真正要操作列,只动 df.columns;要操作行,才用 reset_index() 或 set_index()。
- 确认对象:打印
type(df.columns),是pandas.core.indexes.multi.MultiIndex才需展平列 - 验证结果:展平后运行
df.columns.is_unique,必须为True,否则后续按列名选列会出错
从 Excel 读入时就避免多级表头
如果源头是 Excel,且表头只有前两行有意义,用 pd.read_excel(..., header=[0,1]) 明确指定层级,比之后展平更稳妥。但如果 Excel 表头不规整(比如第三行有合并单元格、空行),Pandas 仍可能推断出三层甚至四层 MultiIndex。
- 优先尝试
header=0读单层,再用df.iloc[:2]手动构造列名 - 用
skiprows跳过干扰行,比依赖自动解析更可靠 - 读入后立刻检查
df.columns.nlevels,大于 1 就得处理
展平不是终点,而是为了后续能用 df['col_name'] 或 df.filter(regex='...') 正常工作。只要列名变成干净、唯一、合法的字符串,下游所有操作才不会意外中断。

















