
本文介绍如何利用 pandas 的 melt 方法高效地将含多个 ID 列(如 Applct_Id_1/2/3)的宽表展开为长表,仅保留非空 ID 值并自动复制对应行,适用于 40 万级数据量的生产场景。
本文介绍如何利用 pandas 的 `melt` 方法高效地将含多个 id 列(如 applct_id_1/2/3)的宽表展开为长表,仅保留非空 id 值并自动复制对应行,适用于 40 万级数据量的生产场景。
在实际数据分析中,常遇到“一行多标识”的宽表结构:单条主记录(如 APPN)关联多个可选 ID 字段(Applct_Id_1/2/3),需将每个非空 ID 展开为独立记录,同时保留原始行的其他属性(如 Name、Age)。直接循环或 apply 易导致性能瓶颈,而 pd.melt() 是专为此类操作设计的向量化方案,兼具简洁性与高性能。
核心步骤如下:
指定固定列(id_vars)与待展开列(value_vars)
将不变的主键和属性列(APPN, Name, Age)设为 id_vars;将需展开的 ID 列(Applct_Id_1, Applct_Id_2, Applct_Id_3)传入 value_vars。执行 melt 并过滤空值
melt 会生成三列:id_vars 原样复制、var_name(原列名)、value_name(原值)。立即用 .notna() 过滤掉 ID_Number 为空的行,避免冗余计算。清理与排序(可选)
删除无用的 ID_Type 列(原列名),按 APPN 和 ID_Number 排序提升可读性——此步对结果逻辑无影响,但便于调试与后续分组。
以下是完整、可直接运行的优化代码:
import pandas as pd
df = pd.DataFrame({
'APPN': [1001, 1002, 1003, 1004, 1005, 1006],
'Applct_Id_1': ['A', 'B', 'C', 'D', None, 'F'],
'Applct_Id_2': [None, 'E', 'F', None, 'G', None],
'Applct_Id_3': ['W', 'Z', None, 'Y', None, None],
'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve', 'Frank'],
'Age': [25, 30, 35, 40, 45, 50]
})
# 高效展开:一步 melt + 过滤 + 清理
result = (df.melt(
id_vars=['APPN', 'Name', 'Age'],
value_vars=['Applct_Id_1', 'Applct_Id_2', 'Applct_Id_3'],
var_name='ID_Type',
value_name='ID_Number'
)
.dropna(subset=['ID_Number']) # 更简洁的空值过滤写法
.drop(columns=['ID_Type'])
.sort_values(['APPN', 'ID_Number'], ignore_index=True)
)
print(result)
关键优势说明:
✅ 向量化性能:melt 底层基于 NumPy,处理 40 万行时耗时通常在毫秒级,远优于 iterrows() 或 explode(后者需先构造 list)。
✅ 内存友好:链式操作(.dropna().drop().sort_values())避免中间变量,减少内存占用。
✅ 扩展性强:新增 ID 列只需更新 value_vars 列表,无需重写逻辑。
注意事项:
- 若 ID 列存在字符串 'None'(非 Python None),需预先用 df.replace('None', pd.NA) 转换,否则 dropna 不生效;
- ignore_index=True 在 sort_values 中确保索引连续,便于后续索引访问;
- 如需统计每 APPN 的 ID 数量,可在 result 上执行 result.groupby('APPN').size() —— 此结果天然支持后续聚合分析。
该方法已验证可稳定处理数十万至百万级数据,在保持代码可读性的同时,兼顾工程效率与维护性。











