一句话总结
melt()将宽表转为长表(多列折叠为两列),wide_to_long()将多组相似列名宽表转为长表(适合重复测量数据)。
1. melt() — 宽表 → 长表
将多个列”熔化”为两列:一列是变量名(原列名),一列是变量值。
基本语法
DataFrame.melt(id_vars=None, value_vars=None, var_name=None,
value_name='value', col_level=None, ignore_index=True)参数详解
| 参数 | 说明 |
|---|---|
id_vars | 保持不变的标识列(作为每行的唯一标识),可以是列名或列表 |
value_vars | 要折叠为”变量-值”的列。默认折叠除 id_vars 外的所有列 |
var_name | 变量列的列名,默认 'variable' |
value_name | 值列的列名,默认 'value' |
col_level | 若列为 MultiIndex,指定要熔化的层级 |
ignore_index | 是否对结果行重新编号(0,1,2,…),默认 True;False 时保留原索引 |
示例:基本熔解
import pandas as pd
df = pd.DataFrame({
'name': ['Alice', 'Bob'],
'math': [90, 85],
'english': [88, 92],
'physics': [95, 78]
})
melted = df.melt(id_vars='name', var_name='subject', value_name='score')
print(melted)
# name subject score
# 0 Alice math 90
# 1 Bob math 85
# 2 Alice english 88
# 3 Bob english 92
# 4 Alice physics 95
# 5 Bob physics 78示例:指定 value_vars
df.melt(id_vars='name', value_vars=['math', 'physics'],
var_name='subject', value_name='score')
# 仅折叠 math 和 physics 两列示例:col_level(多层列索引)
df_cols = pd.DataFrame(np.random.rand(3, 4),
columns=pd.MultiIndex.from_product([['math', 'english'], ['期中', '期末']]))
df_cols.melt(col_level=0) # 只熔最外层示例:ignore_index
df.melt(id_vars='name', ignore_index=False)
# 保留原 DataFrame 的行索引作为结果索引2. wide_to_long() — 宽表 → 长表(带 stubnames)
针对列名有共同前缀(stubname)的宽表,一次性将多组列转换为长表。
基本语法
pd.wide_to_long(df, stubnames, i, j, sep='', suffix=r'\d+')参数详解
| 参数 | 说明 |
|---|---|
stubnames | 列名前缀(stub),字符串或字符串列表 |
i | 标识列(id 列),类似于 melt 中的 id_vars |
j | 新生成的”后缀变量”列名(如年份、时间点) |
sep | 前缀与后缀的分隔符,默认 ''(如 'math1');若列名为 'math_1' 则 sep='_' |
suffix | 识别后缀的正则表达式,默认 r'\d+'(数字) |
示例:基本用法
df_wide = pd.DataFrame({
'id': [1, 2, 3],
'name': ['A', 'B', 'C'],
'math_1': [90, 85, 88],
'math_2': [92, 80, 90],
'eng_1': [88, 92, 85],
'eng_2': [95, 89, 82]
})
long = pd.wide_to_long(df_wide, stubnames=['math', 'eng'],
i='id', j='year', sep='_', suffix=r'\d+')
print(long)
# name math eng
# id year
# 1 1 A 90 88
# 2 A 92 95
# 2 1 B 85 92
# 2 B 80 89
# 3 1 C 88 85
# 2 C 90 82示例:多列 stubnames
df_panel = pd.DataFrame({
'subject': [1, 2, 3],
'pre_test': [10, 12, 11],
'post_test': [15, 18, 17],
'pre_score': [5.5, 6.0, 5.8],
'post_score':[8.0, 8.5, 7.9]
})
result = pd.wide_to_long(df_panel,
stubnames=['pre', 'post'], # 两个前缀
i='subject', j='phase', sep='_')3. 长宽转换的互逆关系
# melt 的"逆操作" ≈ pivot
restored = melted.pivot(index='name', columns='subject', values='score')
# wide_to_long 的"逆操作" ≈ pivot + 列名拼接
restored2 = long.reset_index().pivot(index='id', columns='year', values='math')
restored2.columns = [f'math_{c}' for c in restored2.columns]4. 实战:宽表转 tidy data
标准数据整理流程
# 原始宽表:每个学生一行,多门课多学期 raw = pd.DataFrame({ 'student': ['Alice', 'Bob'], 'math_2023': [85, 90], 'math_2024': [92, 88], 'eng_2023': [80, 78], 'eng_2024': [85, 82] }) # 方法 1:melt + 拆分列 step1 = raw.melt(id_vars='student', var_name='variable', value_name='score') step1[['subject', 'year']] = step1['variable'].str.split('_', expand=True) tidy = step1.drop(columns='variable') # 方法 2:wide_to_long 一步到位 tidy2 = pd.wide_to_long(raw, stubnames=['math', 'eng'], i='student', j='year', sep='_')
注意事项
melt()后原来的各列数据类型可能不一致,折叠后变为同一列,可能会被提升为object类型,必要时使用pd.to_numeric()转换。wide_to_long()要求列名遵循stubname + sep + suffix的固定模式,不满足时需先rename。