
本文详解如何将一个宽格式 DataFrame(列名为年份)中的数值,根据另一个 DataFrame 的两列(如 NAME 和 YEAR)动态查表填充为新列,核心是利用 set_index().stack() 实现行列联合索引映射。
本文详解如何将一个宽格式 dataframe(列名为年份)中的数值,根据另一个 dataframe 的两列(如 name 和 year)动态查表填充为新列,核心是利用 `set_index().stack()` 实现行列联合索引映射。
在实际数据分析中,常遇到「宽表查值」场景:一个 DataFrame(如 df2)以实体(如人名)为行、属性(如年份)为列,存储标量值;而另一个 DataFrame(如 df1)仅含实体与属性标识(如 NAME 和 YEAR),需据此从宽表中提取对应值生成新列(如 SCORE)。直接 merge 无法解决——因为 df2 的年份是列名而非数据列,需先将其“拉直”为长格式。
关键思路:将宽表 df2 转换为(NAME, YEAR, SCORE)三元组长格式,再与 df1 按 NAME 和 YEAR 双键合并。
最简洁高效的方法是使用 set_index().stack():
# 步骤1:将 df2 转为长格式(NAME + 年份列 → (NAME, YEAR) 索引 + SCORE 值)
df2_long = (df2
.set_index('NAME') # 以 NAME 为行索引
.stack() # 将所有年份列(2000/2001/2002)压入行,生成 MultiIndex (NAME, YEAR)
.reset_index(name='SCORE') # 展开索引为普通列,并重命名值列为 'SCORE'
.rename(columns={'level_1': 'YEAR'}) # 将原列名(年份)重命名为 'YEAR'
)
# 步骤2:与 df1 按 NAME 和 YEAR 左连接(确保 df1 行数不变)
result = df1.merge(df2_long, on=['NAME', 'YEAR'], how='left')执行后,result 即为所求的带 SCORE 列的完整 DataFrame。
✅ 优势说明:
- 零循环、纯向量化,性能远超 apply 或嵌套 for;
- stack() 自动处理任意数量的年份列(无需硬编码列名);
- merge 保证匹配逻辑清晰、可处理缺失值(how='left' 保留 df1 所有行,未匹配处 SCORE 为 NaN)。
⚠️ 注意事项:
- df2 中年份列名必须为字符串或可转换为字符串的类型(如整数 2000 会被自动转为 '2000'),且需与 df1['YEAR'] 数据类型一致(建议统一为 str 或 int,避免隐式转换失败);
- 若 df2 存在重复 NAME 行,set_index 会报错,需先去重或聚合(如 df2.groupby('NAME').first());
- stack() 默认忽略 NaN,若宽表中存在空值且需保留,可加参数 dropna=False。
此方法本质是 Pandas 的「透视逆操作」,比手动 melt 更简练——它绕过显式指定 value_vars,直接利用列名作为第二索引维度,是处理此类行列联合查找问题的标准范式。










