
本文介绍使用 pandas.melt() 配合布尔索引,将编号列(如 '1', '2', '3')智能堆叠为一列 value:仅保留列名与 id 值相等的单元格,其余丢弃,完美实现“按 ID 动态取值”的宽转长操作。
本文介绍使用 `pandas.melt()` 配合布尔索引,将编号列(如 '1', '2', '3')智能堆叠为一列 `value`:仅保留列名与 `id` 值相等的单元格,其余丢弃,完美实现“按 id 动态取值”的宽转长操作。
在数据处理中,常遇到一种特殊宽表结构:前两列为标识字段(如 id 和 date),后续多列为以数字命名的“ID 映射列”(如 '1', '2', '3', …, '100'),每行中仅对应其 id 列号的那列有有效值,其余列为空或冗余。目标是将这些编号列“折叠”为单一 value 列,且确保每个 (id, date) 组合只取其 id 所指列的值。
直接调用 stack() 或简单 melt() 无法满足该逻辑——因为我们需要的是条件性提取,而非无差别堆叠。正确解法分三步:
-
melt()宽转长:以['id', 'date']为标识变量,将所有编号列(var_name='id2')转为行,生成中间表,含id,date,id2(列名字符串),value四列; -
匹配过滤:利用
df.id == id2.astype(int)筛出id与列名数值一致的行(如id=2的行只保留原'2'列的值); -
清理输出:删除临时列
id2,得到最终三列结果。
以下是完整可运行示例:
import pandas as pd
import numpy as np
# 构造示例数据(注意:列名 '1','2','3' 为字符串)
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:熔化,将编号列转为变量
melted = df.melt(id_vars=['id', 'date'], var_name='id2', value_name='value')
# 步骤2:筛选 id 与列名数值相等的行(注意列名是字符串,需转 int)
mask = melted['id'] == melted['id2'].astype(int)
result = melted[mask].drop(columns='id2').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
⚠️ 关键注意事项:
- 编号列名必须为字符串类型(如
'1','2'),若为整数列名(1,2),melt()仍可工作,但astype(int)可省略; - 若存在缺失列名(如
id=4但无'4'列),对应行将被自动过滤掉; - 空值(
NaN)会被自然保留,无需额外处理; - 列名数量巨大时(如
'1'到'500'),该方法依然高效,无需循环。
此方案兼具可读性与健壮性,是处理“ID 驱动列映射”类宽表的标准范式。










