接龙按选项分组统计票数:谁选A谁选B,一个脚本出票数表

2026-10-07 · 喵窝资源铺

场景:部门团餐接龙「A套餐/B套餐/C套餐」,收了三轮——张三先选A第二天改成B、孙七第三轮反悔选C,最后还要回答「A套餐到底几个人订」。人工翻聊天记录数票,改一次票数错一次。这篇给一个脚本(jielong_vote.py):多轮接龙文本 + 选项列表喂进去,关键词归票 + 改票留最新,每个选项票数、人选、改票记录、认不出的行全部列清楚。代码实跑验证,真实输出见下。

完整脚本

先 pip install pandas openpyxl,每轮接龙各自存成 txt,运行 python jielong_vote.py round1.txt round2.txt "A套餐" "B套餐" "C套餐" 票数表.xlsx(选项按顺序排在 txt 后面):

# -*- coding: utf-8 -*-
"""接龙按选项分组统计票数:改票留最新,未识别行标出来不瞎猜。
用法 python jielong_vote.py r1.txt r2.txt [...] "选项1" "选项2" ... out.xlsx"""
import re, sys, difflib
import pandas as pd

def parse(fp):
    rows = []
    for line in open(fp, encoding="utf-8"):
        line = line.strip()
        if not line:
            continue
        line = re.sub(r'^[\d0-9]+\s*[\.,、\)).]\s*', '', line)  # 剥行首编号(含全角)
        line = line.replace(':', ':')
        m = re.match(r'([一-龥A-Za-z·]{2,8})\s*[::,,]?\s*(.*)', line)  # 冒号可选
        if not m:
            continue
        name, rest = m.group(1), m.group(2).strip()
        if not rest:
            continue
        rows.append({'姓名': name, '选择': rest})
    return rows

files = [a for a in sys.argv[1:] if a.endswith('.txt')]
out = sys.argv[-1]
options = [a for a in sys.argv[1:-1] if not a.endswith('.txt')]

votes, changes = {}, []   # votes: 姓名 -> (选择, 轮次)
for rnd, fp in enumerate(files, 1):
    for r in parse(fp):
        prev = votes.get(r['姓名'])
        if prev is not None and prev[0] != r['选择']:
            if prev[1] != rnd:  # 同轮内重复出现=确认,不算改票
                changes.append((r['姓名'], prev[0], r['选择'], rnd))
        votes[r['姓名']] = (r['选择'], rnd)

def classify(text):
    hits = [o for o in options if o in text]
    if len(hits) == 1:
        return hits[0]
    if not hits:
        best = difflib.get_close_matches(text, options, cutoff=0.4)
        if len(best) == 1:
            return best[0]
        return f'??未识别({text})'
    return f'??多选({text})'   # 同时提到两个选项,交人工

tally = {o: [] for o in options}
for name, (sel, _) in votes.items():
    tally.setdefault(classify(sel), []).append(name)

rows = [{'选项': o, '票数': len(p), '人选': '、'.join(p)}
        for o, p in tally.items()]
df = pd.DataFrame(rows).sort_values('票数', ascending=False)
df.to_excel(out, index=False)
print(f'共 {len(files)} 轮,有效投票 {len(votes)} 人 -> {out}')
print(df.to_string(index=False))
for name, old, new, rnd in changes:
    print(f'--- 改票(留最新) ---\n  {name}: 第{rnd}轮「{old}」改为「{new}」')
for name, (sel, _) in votes.items():
    c = classify(sel)
    if c.startswith('??'):
        print(f'--- 未识别需人工 ---\n  {name}: 「{sel}」 {c}')

实跑:4 轮 14 行 → 12 人投票,改票全揪出

造了 4 轮接龙共 14 行,故意埋了:跨轮改票、改票说明里带理由、行首全角编号、无冒号行、混进一条管理员通知、以及一条「不要A套餐了,换B套餐」的改票(一句话提到两个选项)。跑一遍的输出:

共 4 轮,有效投票 12 人 -> doc_out2.xlsx
               选项  票数          人选
              A套餐   4 王五、周八、郑十、陈二
              B套餐   3    张三、李四、钱二
              C套餐   3    赵六、孙七、吴九
   ??未识别(管理员发的通知)   1       这不是接龙
??多选(不要A套餐了,换B套餐)   1          刘一
--- 改票(留最新) ---
  张三: 第2轮「A套餐」改为「改成B套餐(A太贵了)」
--- 改票(留最新) ---
  孙七: 第3轮「B套餐」改为「改C套餐」
--- 未识别需人工 ---
  这不是接龙: 「管理员发的通知」 ??未识别(管理员发的通知)
--- 未识别需人工 ---
  刘一: 「不要A套餐了,换B套餐」 ??多选(不要A套餐了,换B套餐)

12 人各归各档,A/B/C 票数一目了然;张三改票「A太贵了」这种带理由的照样识别成 B;管理员通知和「一句话提到两个选项」的刘一都标成未识别交人工,绝不硬塞进某个选项。幂等验证:同样输入重跑一遍,读回来的表格逐格一致(比数据不比 xlsx 字节)。

两个真坑(都是这轮实跑踩出来的)

如果接龙的选项不是固定几档(比如自由填数量、带规格),或者要把票数表再跟签到/付款记录对一遍,逻辑要再包一层,可以直接找我们代写(见下)。

懒得自己跑?找我们代写
喵窝资源铺提供 Python 脚本代写服务:你说清楚接龙格式、选项和要什么表,我们写好、用你的真实聊天记录测试通过再交付,24 小时内出活。AI 服务,明码标价,先看效果再付款(虚拟物品不退款,不满意免费重做)。
👉 了解代写服务 | 联系我们

📖 相邻文章:多轮接龙合并去重 · 单轮接龙统计成Excel · 报名名单×签到表核对 · AA收款记录对账 · 聊天记录批量提取待办 · 博客目录

票数表仅供参考,最终以群内原始接龙消息为准;??未识别行务必人工确认后再定档。