
本文介绍如何使用 pandas 对具有相同主键(如 P ID)的重复行进行智能合并,通过前向/后向填充保留每列首个有效值,最终将 3130 行去重压缩为约 900 行结构完整的单行记录。
本文介绍如何使用 pandas 对具有相同主键(如 P ID)的重复行进行智能合并,通过前向/后向填充保留每列首个有效值,最终将 3130 行去重压缩为约 900 行结构完整的单行记录。
在数据清洗与报表生成中,常遇到“逻辑重复但信息互补”的情况:多行共享相同主键(如 P ID、T ID、C ID),但各字段(如 Q1、Q2、Q3)的非空值分散在不同行。直接使用 drop_duplicates() 会丢失信息,而简单取 'first' 或 'last' 又存在不确定性——你真正需要的是按组聚合时,对每个字段取其首个非空值(即“拼合”各行的有效信息)。
✅ 推荐方案:bfill() + head(1)(推荐用于本场景)
该方法利用“向后填充(back-fill)”将每组内后续行的非空值向上补全至首行,再取首行,从而安全提取每列的首个有效值:
import pandas as pd
# 示例数据(模拟原始 Excel 导入结果)
df = pd.DataFrame({
"P ID": [318, 318, 319, 319],
"T ID": [495, 495, 496, 496],
"C ID": ["00036282", "00036282", "00036283", "00036283"],
"Q1": ["NO", None, "Yes", None],
"Q2": [None, "Yes", None, "Yes"],
"Q3": [None, "All cost covered", "No additional costs", None],
})
# 核心操作:按 P ID 分组 → 每组内向后填充 → 取填充后首行
merged_df = (df.groupby("P ID", sort=False)
.apply(lambda x: x.bfill().head(1))
.reset_index(drop=True))
print(merged_df)输出:
立即学习“Python免费学习笔记(深入)”;
P ID T ID C ID Q1 Q2 Q3 0 318 495 00036282 NO Yes All cost covered 1 319 496 00036283 Yes Yes No additional costs
✅ 优势:不依赖字段类型(字符串/数值/空值兼容)、语义清晰(取“最早出现的有效回答”)、结果确定且可复现。
? 替代方案:first() 配合 fillna()(适用于需自定义填充逻辑)
若需更精细控制(例如仅对特定列启用填充),可先用 fillna(method='bfill') 预处理,再调用 first():
merged_df_alt = (df.groupby("P ID", sort=False)
.apply(lambda x: x.fillna(method='bfill').iloc[0])
.reset_index(drop=True))或逐列指定聚合方式(更显式,适合混合类型字段):
agg_funcs = {col: 'first' for col in df.columns if col != "P ID"}
# 注意:'first' 在 pandas 中会自动跳过 NaN,等效于取首个非空值
merged_df_explicit = df.groupby("P ID", sort=False).agg(agg_funcs).reset_index()⚠️ 重要注意事项:
- sort=False 参数建议显式添加,避免因默认排序打乱原始组内顺序,影响 bfill/first 的行为;
- 若存在多组完全空值(如某 Q3 列全为 NaN),first() 和 bfill().head(1) 均会返回 NaN,符合预期;
- 确保分组键(如 "P ID")拼写与原始 DataFrame 列名严格一致(注意大小写与空格);
- 处理前建议执行 df.isnull().sum() 检查空值分布,验证策略合理性;
- 最终导出 Excel 时,使用 merged_df.to_excel("cleaned_output.xlsx", index=False) 即可获得目标格式。
通过上述方法,你不仅能将 3130 行原始数据精准压缩为约 900 行无损合并结果,还可确保所有字段(包括 T ID、C ID 等辅助键)保持一致性——真正实现自动化、可验证、可复现的数据整合。










