
本文介绍使用pandas将宽格式DataFrame(年份为列名)中的值,根据另一DataFrame中对应的行标签(如NAME)和列名(如YEAR)动态映射到新列的高效方法,避免循环,核心依赖set_index().stack()与merge()组合。
本文介绍使用pandas将宽格式dataframe(年份为列名)中的值,根据另一dataframe中对应的行标签(如name)和列名(如year)动态映射到新列的高效方法,避免循环,核心依赖`set_index().stack()`与`merge()`组合。
在实际数据分析中,常遇到“宽表→长表”匹配场景:一个DataFrame(如df2)以类别(NAME)为行索引、时间维度(如2000, 2001, 2002)为列名存储指标值;另一个DataFrame(如df1)则包含相同类别的多条记录及对应的时间标签(YEAR),需据此查表生成新字段(如SCORE)。直接merge无法解决列名参与匹配的问题,此时应先将宽表重塑为规范的长格式,再执行标准连接。
关键步骤如下:
- 将宽表df2转换为长格式:使用set_index("NAME").stack()将列名(2000, 2001, 2002)转为行索引的第二级,并展开为一列;再用reset_index()恢复为普通列,并重命名新列;
- 与df1按NAME和YEAR合并:确保两表具有完全一致的键字段名(如将堆叠后的层级列命名为YEAR),即可通过merge完成精准映射。
以下是完整可运行示例代码:
import pandas as pd
# 构造示例数据
df1 = pd.DataFrame({
"NAME": ["Anna", "Mary", "Paul", "Paul", "Mary", "Anna", "Anna", "Mary", "Paul"],
"YEAR": [2002, 2001, 2000, 2001, 2002, 2001, 2000, 2000, 2002]
})
df2 = pd.DataFrame({
"NAME": ["Anna", "Mary", "Paul"],
2000: [5, 3, 2],
2001: [4, 2, 3],
2002: [3, 5, 4]
})
# 步骤1:重塑df2为长格式
df2_long = (df2.set_index("NAME")
.stack()
.reset_index(name="SCORE")
.rename(columns={"level_1": "YEAR"}))
# 步骤2:与df1合并
result = df1.merge(df2_long, on=["NAME", "YEAR"])
print(result)输出结果为:
NAME YEAR SCORE 0 Anna 2002 3 1 Mary 2001 2 2 Paul 2000 2 3 Paul 2001 3 4 Mary 2002 5 5 Anna 2001 4 6 Anna 2000 5 7 Mary 2000 3 8 Paul 2002 4
✅ 注意事项:
- stack()默认会将所有非索引列压平,若df2含多余列,建议先select_dtypes(include='number')过滤或显式指定列;
- YEAR列在df2_long中默认为str类型(若原始列为整数,stack()后仍保持原类型),但务必确保与df1["YEAR"]类型一致,否则merge可能静默失败——可统一用astype(int)校验;
- 替代方案包括melt()(更语义化),但set_index().stack()在处理单指标宽表时更简洁;若需保留多变量,应优先选用melt(id_vars="NAME", var_name="YEAR", value_name="SCORE")。
该方法完全向量化,性能远优于Python循环,是pandas处理“列名即条件”的标准范式。










