melt()转长表需显式指定id_vars和value_vars,避免漏写或误填;pivot()转宽表要求index+columns组合唯一,否则报错;重复时应改用pivot_table()并设aggfunc;转换后需手动恢复datetime、category等类型。

用 melt() 把宽表变长表:关键在 id_vars 和 value_vars
宽表转长表,核心是明确哪些列是“标识列”(比如用户ID、日期),哪些是“值列”(比如不同产品的销量)。melt() 不会自动猜——你必须显式指定。
常见错误是漏写 id_vars,结果所有列都被当成了变量,生成一堆无意义的 variable 行;或者把不该进 value_vars 的列(比如备注字段)塞进去,导致数据错位。
-
id_vars:必须是字符串列表,哪怕只有一列也要写成['user_id'],不能写成'user_id'(会报TypeError: unhashable type: 'str') -
value_vars可省略,不写时默认取除id_vars外所有列;但显式写出更安全,尤其当原始列名含空格或特殊字符时 - 生成的默认列名是
variable和value,用var_name和value_name参数可改,比如var_name='product'、value_name='sales'
示例:
df.melt(id_vars=['date', 'region'], value_vars=['A_sales', 'B_sales'], var_name='product', value_name='sales')
用 pivot() 把长表变宽表:索引、列、值三者缺一不可
pivot() 不是万能的——它要求输入长表中,index + columns 的组合必须唯一,否则直接报 ValueError: Index contains duplicate entries, cannot reshape。这是最常卡住人的地方。
它不聚合,只是“摊开”。如果你的长表里同一组 index+columns 有多个 values(比如同一天同一产品有两条销售记录),pivot() 就会失败;这时得先用 pivot_table()。
-
index和columns必须是现有列名,不能是计算字段(如df['date'].dt.year需先赋值为新列再传入) -
values参数可选,但省略后若存在多个数值列,会为每个都生成一套宽列,容易混乱 - 生成结果默认是
NaN填充缺失,不会自动插值或补0——需要填充得自己接.fillna(0)或.ffill()
示例:
df.pivot(index='date', columns='product', values='sales')
pivot_table() 是 pivot() 的安全替代:遇到重复键就靠它
只要长表里 index+columns 组合不唯一,就别硬刚 pivot(),直接换 pivot_table()。它本质是分组+聚合,天然处理重复。
默认聚合函数是 np.mean,但多数场景你要的是 sum 或 first;不指定 aggfunc 容易得到小数结果,和预期不符。
-
aggfunc接字符串(如'sum')或函数(如lambda x: x.iloc[0]),多值聚合可传字典:{'sales': 'sum', 'qty': 'max'} -
fill_value参数比pivot()的.fillna()更早生效,适合直接填 0 或空字符串 - 性能上比先
drop_duplicates()再pivot()更稳,尤其数据量大时
示例:
df.pivot_table(index='date', columns='product', values='sales', aggfunc='sum', fill_value=0)
长宽互转后索引和列类型容易丢:datetime 和 category 别被自动转成 object
转换过程会重置索引和列结构。melt() 后原 datetime 列若进了 value_vars,就变成普通 object;pivot() 后新生成的列名默认是 object 类型,哪怕原来是 category 或数字。
这不是 bug,是 pandas 的默认行为——它优先保数据安全,不自动推断语义。但后续做时间切片或分类统计时就会出问题。
-
melt()前,对要保留类型的列先用astype()显式固定,比如df['date'] = pd.to_datetime(df['date']) -
pivot()或pivot_table()后,立刻检查新列类型:df.columns.dtype;若是object,且本应是类别,就用df.columns = df.columns.astype('category') - 时间索引转宽表后消失?用
set_index('date')手动恢复,别依赖自动继承
这事不难,但容易在调试完逻辑后才想起来,结果 downstream 操作全报错。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











