2026-10-07 · 喵窝资源铺
场景:活动收了 10 人报名,当天签到表 9 人。人工拿两张 Excel 对名字,对到眼花还漏一个,事后被问「吴十怎么没来」答不上来。更麻烦的是签到机/手写签到会把名字打错——「周九」变成「周玫」,肉眼核对基本靠缘分。这篇给一个脚本(roster_checkin_diff.py):报名表和签到表一起喂进去,同名匹配 + 手机号兜底 + 形近字模糊匹配三层对齐,输出「报了没来」「没报名却签到」两张名单。代码实跑验证,真实输出见下。
🎁 免费模板:下载报名×签到核对模板(报名表+签到表+说明页,示例行可直接跑通,删掉示例换成你的数据即可)。
先 pip install pandas openpyxl,报名表和签到表各存一份 xlsx(都要有「姓名」列,报名表建议带「手机号」列),运行 python roster_checkin_diff.py 报名表.xlsx 签到表.xlsx 核对结果.xlsx:
# -*- coding: utf-8 -*-
"""报名名单×签到表交叉核对:找出报了名没来/没报名却来了的人。
用法 python roster_checkin_diff.py 报名表.xlsx 签到表.xlsx"""
import sys
import pandas as pd
from difflib import SequenceMatcher
roster_fp, checkin_fp, out_fp = sys.argv[1], sys.argv[2], sys.argv[3]
def norm_name(s):
return str(s).strip().replace(" ", "")
# 手机号列必须显式 dtype=str,否则 13812345678 会被读成 1.381235e+10
roster = pd.read_excel(roster_fp, dtype={"手机号": str})
checkin = pd.read_excel(checkin_fp, dtype=str)
rmap = {norm_name(n): i for i, n in enumerate(roster["姓名"])}
cmap = {norm_name(n): i for i, n in enumerate(checkin["姓名"])}
# 手机号兜底匹配:名字对不上但手机号对得上,也算同一人
r_phone = {str(p).strip(): n for n, p in zip(roster["姓名"], roster.get("手机号", [""] * len(roster)))}
c_phone = {str(p).strip(): n for n, p in zip(checkin.get("手机号", [""] * len(checkin)), checkin["姓名"])}
matched_pairs = [] # (报名名, 签到名, 方式)
no_show = list(rmap) # 先假设全没来
used_c = set()
for cn, ci in cmap.items():
if cn in rmap:
matched_pairs.append((cn, cn, "同名")); used_c.add(cn); no_show.remove(cn)
for cp, cn in c_phone.items():
if cn in no_show and cp in r_phone and r_phone[cp] == cn:
matched_pairs.append((cn, cn, "手机号")); used_c.add(cn); no_show.remove(cn)
# 剩余的做模糊匹配(签到机把名字打错字)
fuzzy = []
for cn in list(cmap):
if cn in used_c: continue
best, score = None, 0.0
for rn in no_show:
s = SequenceMatcher(None, cn, rn).ratio()
if s > score: best, score = rn, s
# 两字名错一个字相似度只有 0.5,阈值必须按名字长度分层,不然形近字全漏
# 全员到齐 + 有陌生人签到时 no_show 已空、best 为 None,必须先判空再取长度
thr = 0.5 if best and len(best) <= 2 else 0.75
if best and score >= thr:
fuzzy.append((best, cn, f"模糊{score:.2f}")); used_c.add(cn); no_show.remove(best)
walk_in = [cn for cn in cmap if cn not in used_c] # 没报名却签到的
res = pd.DataFrame(
[{"姓名": n, "状态": "未签到"} for n in no_show]
+ [{"姓名": n, "状态": "已签到"} for n in cmap if n in used_c]
)
res.to_excel(out_fp, index=False)
print(f"报名 {len(rmap)} 人 / 签到 {len(cmap)} 人 -> {out_fp}")
print(f"对上 {len(matched_pairs)+len(fuzzy)} 人(同名/手机号 {len(matched_pairs)},模糊 {len(fuzzy)})")
for r, c, how in fuzzy: print(f" 模糊匹配: 报名「{r}」≈ 签到「{c}」({how})")
print(f"--- 报了没来 {len(no_show)} 人 ---"); [print(" ", n) for n in no_show]
print(f"--- 没报名却签到 {len(walk_in)} 人 ---"); [print(" ", n) for n in walk_in]
造了两张表,故意埋了:报名表里「王小明 」带尾随空格、签到表里「周九」被签到机打成「周玫」、2 人报了没来(吴十、钱二)、1 人没报名却签到(刘二狗)。跑一遍的输出:
报名 10 人 / 签到 9 人 -> out_check.xlsx 对上 8 人(同名/手机号 7,模糊 1) 模糊匹配: 报名「周九」≈ 签到「周玫」(模糊0.50) --- 报了没来 2 人 --- 吴十 钱二 --- 没报名却签到 1 人 --- 刘二狗
「王小明 」的尾随空格被 norm_name 归一化后正常同名匹配;签到机打错的「周玫」靠模糊匹配抓回来并明确标出(人工确认——周玫和周九是不是同一人要人拍板,脚本只负责把嫌疑对象摆上台面);吴十、钱二精确落在「报了没来」,刘二狗落在「没报名却签到」。核对结果同时写了一份 xlsx(每人一行,标未签到/已签到)。幂等验证:同样的输入重跑,读回来的结果表逐格一致(True)。
pd.read_excel 默认读成数字,13812345678 变 1.381235e+10,手机号兜底匹配一条都对不上。读取时显式 dtype={"手机号": str}(写进 Excel 时的坑见上一篇接龙合并——写入用 openpyxl 整列设 @ 文本格式,两边配套才闭环)。模糊匹配的结果只是「嫌疑」不是「结论」:周玫/周九到底是不是同一人,需要拿手机号或现场照片人工确认。脚本负责把可疑名单列出来,别直接拿它当考勤处罚依据。
懒得自己跑?找我们代写
喵窝资源铺提供 Python 脚本代写服务:你说清楚两张表的格式和核对规则,我们写好、用你的真实数据测试通过再交付,24 小时内出活。AI 服务,明码标价,先看效果再付款(虚拟物品不退款,不满意免费重做)。
👉 了解代写服务 | 联系我们
📖 相邻文章:报名表×付款记录对账 · 接龙按选项分组统计票数 · 多轮微信群接龙合并去重 · 单轮接龙统计成Excel · 考勤打卡×排班表交叉核对 · 两张表人名模糊匹配合并 · 博客目录
核对结果仅供参考,最终出勤认定请以现场记录为准。