melt()是最直接的宽转长方式,需显式指定id_vars保留标识列,value_vars可选但建议显式列出,var_name和value_name应重命名以提升语义;多级列名时优先用stack()再reset_index();Pandas 2.0+可用pivot_longer()支持正则匹配列名;转长后须检查dtypes和缺失值。

用 melt() 是最直接的宽转长方式
绘图库(比如 Matplotlib、Seaborn)普遍要求数据是「长格式」:每行一个观测,变量名和值分列两列。Pandas 的 melt() 就是专干这事的,它不依赖索引结构,语义清晰,出错率低。
常见错误是漏掉 id_vars——比如你有年份、地区列作为标识,却没把它们设为固定列,结果全被“熔”进去了,绘图时坐标轴就乱了。
-
id_vars必须显式指定哪些列保持不动(如['year', 'region']),否则默认全当 value 列处理 -
value_vars可选,不填就自动取除id_vars外的所有列;但建议显式列出,避免新增列后逻辑意外变更 -
var_name和value_name用来重命名新生成的变量列和值列,默认是'variable'和'value',绘图前最好改成语义名,比如var_name='category'
示例:
df_long = df.melt(id_vars=['date'], var_name='product', value_name='sales')
遇到多级列名时,先 stack() 再 reset_index()
如果宽表列是 MultiIndex(比如按年份+季度嵌套),melt() 会把整个元组当列名,生成难读的字符串(如 "(2023, 'Q1')")。这时更稳妥的是用 stack(),它天然支持层级折叠。
注意 stack() 默认压栈最内层列,若要压其他层,得传 level= 参数;压完是 Series,必须接 reset_index(name='...') 才能还原成 DataFrame。
- 压栈后列名会变成索引,
reset_index()是必须步骤,否则绘图库读不到字段名 - 如果原列名层级含空格或特殊字符,
stack()不报错但后续列访问容易出问题,建议提前用rename(columns=str.strip)清理 - 性能上,
stack()比melt()略慢,但对万行级数据差异可忽略
示例:
df_long = df.stack([0, 1]).reset_index(name='value')
(假设列是两层 MultiIndex)
用 pivot_longer()(pandas >= 2.0)更接近 tidyverse 风格
新版 Pandas 加入了 pivot_longer(),语法比 melt() 更灵活,尤其适合按正则或列名模式批量选列(比如所有以 '_2023' 结尾的列)。
但它不是完全向后兼容的替代品:旧版没这个函数,且部分参数名和行为与 melt() 不同,混用容易困惑。
- 用
names_pattern可以直接拆分列名,比如r'(\w+)_(\d{4})'能同时提取产品名和年份到新列 -
ignore_index=False保留原始索引,对时间序列绘图很重要;默认是True,会丢掉原有行号 - 如果列名含数字开头(如
'2023_sales'),正则需加^锚定,否则可能匹配失败
示例:
df_long = df.pivot_longer(cols=df.columns.str.contains('_2023'), names_to=['metric', 'year'], names_pattern=r'(.+)_(\d{4})', values_to='value')
绘图前务必检查 dtypes 和缺失值
宽转长后最常踩的坑不是结构,而是类型崩塌:原来整数的销量列,转完变成 object(因为混入了 NaN 或字符串);或者时间列被当成普通字符串,seaborn.lineplot() 直接画不出趋势线。
缺失值也容易被忽略——melt() 和 stack() 都默认保留 NaN,但很多绘图函数会静默跳过,导致数据量对不上。
- 转完立刻跑
df_long.dtypes,重点看数值列是否还是float64或int64 - 用
df_long.isna().sum()查每列缺失量,别只信df_long.dropna()——有些场景需要插值或标记,而非直接删 - 时间类列若已转为字符串,用
pd.to_datetime()强制转换,别依赖绘图库自动推断
类型错一次,后面所有图表都可能偏移或报错,这步没法跳过。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











