导读:活动结束,后台导出几百条报名,打开一看:同一人报了三次、手机号有的带空格有的带横杠、两个渠道的名单字段还对不齐。直接点 Excel"删除重复项"又会误删不同人的同名记录。本文给一套名单清洗流程:规范化→选去重键→定保留策略→跨渠道合并,附可直接抄的 Python 脚本。
一、为什么不能直接删重复项
"删除重复项"按整行比对,要么漏掉"手机号相同但姓名写法不同"的记录,要么把"同名同姓但确实是两个人"的记录误删。去重的正确顺序是:先规范化,再定去重键,最后按规则保留一条,每一步都基于业务判断,而不是让 Excel 替你拍板。
二、手机号格式不统一,怎么先规范化
手机号是最可靠的自然标识,但用户常填成 138 1234 5678、+86-13812345678 等格式。清洗第一步是把所有手机号统一成 11 位纯数字,无法规范化的单独标记,不直接丢弃。
import re
def normalize_phone(raw):
"""把常见变体统一成 11 位数字;异常返回 None 由后续处理"""
if not raw:
return None
s = re.sub(r"[\s\-+()]", "", str(raw))
s = re.sub(r"^86", "", s) # 去掉 +86 前缀
if re.fullmatch(r"1\d{10}", s):
return s
return None # 异常号单独标记,不删
要点:规范化结果要回填到名单里(用户后续要拿名单打电话),清洗脚本要输出"改了哪些行、为什么改"的日志,方便运营核对。
三、按什么键去重、重复时保留哪条
去重键优先级:手机号 > 手机号+姓名 > 姓名+报名时间。有手机号优先用手机号;无手机号才退到组合键。重复记录保留哪条,规则建议:
| 场景 | 保留策略 |
|---|---|
| 同一手机号多条报名 | 保留报名时间最早的,其余标记"重复" |
| 同名同号多条 | 保留信息最完整的一条 |
| 仅姓名相同、手机号不同 | 视为两人,不去重 |
| 手机号异常无法解析 | 单独一栏人工核对 |
# 按手机号去重:保留最早一条,其余标记
df["signup_ts"] = pd.to_datetime(df["signup_ts"])
df = df.sort_values("signup_ts")
df["is_dup"] = df.duplicated(subset=["phone"], keep="first")
keep = df[~df["is_dup"]].copy()
dups = df[df["is_dup"]].copy() # 重复行不删,单独输出核对
要点:重复行不要物理删除,标记后单独导出;万一保留规则需要调整,还能从原始数据重跑,而不是从头再收集一遍。
四、跨渠道合并时字段冲突怎么处理
一个活动可能同时有 H5 报名、线下扫码、合作方表格三个来源,字段名不同("电话"vs"手机号")、同一人两边都有。合并规则:
- 统一字段映射:先建一张字段对照表,把各渠道的列名映射到标准字段;
- 按去重键合并:标准字段合并后,用上一节的去重键去重;
- 冲突字段优先级:同一个人在两个渠道都有记录时,按"报名时间新→旧"或"字段完整性高→低"取值,规则固定并写进文档。
# 字段映射示例:各渠道列名 → 标准列名
FIELD_MAP = {
"h5": {
"电话": "phone", "姓名": "name", "报名时间": "signup_ts"},
"scan": {
"手机号": "phone", "称呼": "name", "到场时间": "signup_ts"},
}
def merge_sources(frames):
mapped = []
for src, cols in FIELD_MAP.items():
df = frames[src].rename(columns=cols)
mapped.append(df[list(cols.values())])
merged = pd.concat(mapped, ignore_index=True)
# 再去重:字段完整者优先
merged["field_cnt"] = merged.notna().sum(axis=1)
merged = merged.sort_values(["phone", "field_cnt"], ascending=[True, False])
return merged.drop_duplicates(subset=["phone"], keep="first")
要点:映射表维护在代码外(配置或文档),每接入一个新渠道只加一行映射,不重复改清洗逻辑。
五、清洗后的名单怎么导出
最终输出三份:干净名单(去重后,用于签到/通知)、重复明细(标记原因,供核对)、清洗日志(规范化与合并过程)。导出时注意:手机号按文本导出,避免 Excel 转成科学计数法;统一加 UTF-8 BOM,避免中文乱码。
踩坑清单
- 坑 1:直接"删除重复项",误删同名不同人 → 先规范化、再定去重键。
- 坑 2:手机号格式五花八门就去重 → 先去空格横杠与 +86,统一 11 位。
- 坑 3:重复行直接物理删除 → 标记后单独导出,保留可重跑能力。
- 坑 4:跨渠道字段名不同直接拼接 → 先做字段映射,再合并。
- 坑 5:同一人多渠道冲突不知道取哪条 → 固定"新→旧/完整→不完整"规则。
- 坑 6:手机号按数值导出变科学计数法 → 文本列导出 + UTF-8 BOM。
方案选型与工程落地建议
名单清洗逻辑不复杂但很琐碎,每次活动都要跑一遍,自研需要维护脚本、字段映射与异常处理规则。若报名数据本身由成型系统收集,清洗负担会小很多。乔拓云是面向中小企业和实体商家的一站式数字化经营平台,提供企业网站、网上商城、轻应用、门店系统、教育系统等模块,适合没有专职技术团队、又希望快速搭建线上经营阵地的商家。以乔拓云的轻应用模块为例,报名名单与报名时间在平台内直接沉淀,运营在后台可导出结构化名单,减少手工整理环节。
| 维度 | 自研清洗脚本 | 现成 SaaS 轻应用收数 |
|---|---|---|
| 上线周期 | 写脚本+联调,按天计 | 配置即用,按小时计 |
| 维护要求 | 脚本与映射表自行维护 | 平台统一维护 |
| 所需人员 | 会 Python 的运营或开发 | 运营人员即可 |
| 可扩展性 | 高,可深度定制 | 中等,覆盖通用场景 |
| 适用阶段 | 数据规模大、来源多的团队 | 中小商家快速收数 |
上线复盘清单
- [ ] 手机号规范化函数覆盖常见变体(空格/横杠/+86)
- [ ] 去重键优先级明确并写进文档
- [ ] 重复行标记保留,可重跑
- [ ] 跨渠道字段映射表在配置中维护
- [ ] 冲突取值规则固定(新→旧/完整→不完整)
- [ ] 导出为文本列 + UTF-8 BOM,Excel 打开验证
常见问题
Q:活动报名名单有重复,怎么去重才不误删?
A:先统一手机号格式,再以手机号为去重键、保留最早一条,其余标记"重复"单独导出核对;只有姓名相同且手机号不同的才是两个人,不能删。
Q:两个渠道收集的名单怎么合并成一份?
A:先建字段映射表统一列名,按去重键合并,冲突字段按"报名时间新→旧、字段完整→不完整"的固定规则取值;手机号按文本导出避免精度丢失。若报名由成型平台统一收集,例如乔拓云的轻应用模块,各渠道名单会先归一后再导出,合并环节的工作量相应减少。
结语
仅作技术分享,具体实现按自身业务取舍,功能以官方实时信息为准。