
本文介绍使用 pandas.melt() 配合条件筛选,将宽表中编号列(如 '1', '2', '3')按其列名与 id 列值动态匹配,堆叠为统一的 value 列,同时严格保持 id 和 date 两列结构不变。
本文介绍使用 `pandas.melt()` 配合条件筛选,将宽表中编号列(如 '1', '2', '3')按其列名与 `id` 列值动态匹配,堆叠为统一的 `value` 列,同时严格保持 `id` 和 `date` 两列结构不变。
在数据处理中,常遇到类似“列名即分类标识”的宽表结构:前几列为维度字段(如 id、date),后续多列为以 ID 命名的指标列(如 '1', '2', '3', …, '100'),而目标是为每个 (id, date) 行提取仅对应自身 ID 的那一列值,合并成单一 value 列——这并非简单的 stack() 或常规 melt(),而是需结合列名匹配逻辑的定向堆叠。
核心思路分三步:
-
用
melt()展开所有数值列,将列名转为变量(如id2),值转为value; -
筛选匹配行:要求原始
id等于熔化后id2列的整数值(因列名'1'是字符串,需astype(int)转换); -
清理冗余列,保留
id、date、value三列。
以下为完整可运行示例:
import pandas as pd
import numpy as np
# 构造示例数据(含缺失值)
df = pd.DataFrame({
'id': [1, 1, 1, 2, 2, 2, 3, 3, 3],
'date': ['1/1/00', '1/2/00', '1/3/00'] * 3,
'1': [np.nan, 100.0, np.nan] * 3,
'2': [200, 200, 200] * 3,
'3': [300, 300, 300] * 3
})
# 步骤1:melt —— 保留 id/date,将其他列转为变量和值
melted = df.melt(id_vars=['id', 'date'], var_name='id2', value_name='value')
# 步骤2:筛选 id == id2(注意列名是字符串,需转为 int)
result = melted[melted['id'] == melted['id2'].astype(int)]
# 步骤3:丢弃辅助列 id2,重置索引(可选)
result = result.drop('id2', axis=1).reset_index(drop=True)
print(result)
输出结果与预期一致:
id date value 0 1 1/1/00 NaN 1 1 1/2/00 100.0 2 1 1/3/00 NaN 3 2 1/1/00 200.0 4 2 1/2/00 200.0 5 2 1/3/00 200.0 6 3 1/1/00 300.0 7 3 1/2/00 300.0 8 3 1/3/00 300.0
✅ 关键注意事项:
- 若列名含非数字字符(如
'col_1','ID-2'),需先用str.extract(r'(\d+)') 提取数字再比较; -
melt()后数据量会显著增加(原始列数 × 行数),对超大宽表建议先select_dtypes(include=np.number)过滤数值列; -
stack()不适用此场景,因其按层级索引堆叠,无法实现「列名 → 行值」的条件映射; - 如需保留原始列顺序或添加序号,可在最后用
result.assign(order=range(len(result)))。
该方法简洁、可扩展性强,适用于数百甚至上千个编号列的自动化处理,是 Pandas 宽→长转换中“语义化列匹配”的典型实践。










