#!/usr/bin/env python3 """数据一致性校验脚本。 校验 notebooks/pilot_analysis.md 中的关键数据与 plots/*.csv 数据文件一致, 并验证论文 reports/paper_draft.md 中引用的核心数值与数据源吻合。 用法: python3 processing/validate_data.py """ import csv import os import re import sys ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) PLOTS = os.path.join(ROOT, "plots") NOTEBOOK = os.path.join(ROOT, "notebooks", "pilot_analysis.md") PAPER = os.path.join(ROOT, "reports", "paper_draft.md") errors = [] checks = 0 def ok(msg): global checks checks += 1 print(f" PASS: {msg}") def fail(msg): global errors errors.append(msg) print(f" FAIL: {msg}") def load_csv(name): path = os.path.join(PLOTS, name) with open(path, encoding="utf-8") as f: return list(csv.DictReader(f)) def check_run_data(): print("[1] 校验运行数据 CSV") rows = load_csv("pilot_run_data.csv") cities = [r["city"] for r in rows] if len(cities) == 8: ok(f"地市数 = 8: {', '.join(cities)}") else: fail(f"地市数 = {len(cities)}, 期望 8") producing = [r for r in rows if r["stage"] == "实际产出"] if len(producing) == 2: ok(f"实际产出地市 = 2: {', '.join(r['city'] for r in producing)}") else: fail(f"实际产出地市 = {len(producing)}, 期望 2") huizhou = next(r for r in rows if r["city"] == "惠州") if int(huizhou["alert_count"]) == 19800: ok("惠州预警数据 = 19,800") else: fail(f"惠州预警数据 = {huizhou['alert_count']}, 期望 19800") foshan = next(r for r in rows if r["city"] == "佛山") if int(foshan["alert_count"]) == 3923: ok("佛山预警数据 = 3,923") else: fail(f"佛山预警数据 = {foshan['alert_count']}, 期望 3923") def check_vendor_effort(): print("[2] 校验供应商人天投入 CSV") rows = load_csv("vendor_effort.csv") total = sum(int(r["person_days"]) for r in rows) if total == 100: ok(f"人天合计 = {total}") else: fail(f"人天合计 = {total}, 期望 100") expected = { "需求沟通与前期对接": 15, "现场培训与推广": 20, "技术接入与配置": 18, "问题响应与日常运营": 20, "个性化适配与优化": 12, "差旅在途": 10, "文档编写": 5, } for row in rows: cat = row["category"] days = int(row["person_days"]) if cat in expected and days == expected[cat]: ok(f"{cat} = {days} 人天") else: fail(f"{cat} = {days}, 期望 {expected.get(cat, '?')}") def check_access_cost(): print("[3] 校验接入耗时 CSV") rows = load_csv("access_cost.csv") steps = {r["step"]: r for r in rows} ideal_sum = sum(int(steps[s]["ideal_days"]) for s in ["账号开通", "权限配置", "组织数据匹配", "应用入口配置", "网络策略审批"]) if ideal_sum == 10: ok(f"理想端到端 = {ideal_sum} 工作日") else: fail(f"理想端到端 = {ideal_sum}, 期望 10") actual_sum = sum(int(steps[s]["actual_days"]) for s in ["账号开通", "权限配置", "组织数据匹配", "应用入口配置", "网络策略审批"]) if actual_sum == 18: ok(f"各环节实际合计 = {actual_sum} 工作日(端到端实际约 20,含排队余量)") else: fail(f"各环节实际合计 = {actual_sum}, 期望 18") def check_paper_numbers(): print("[4] 校验论文核心数值") with open(PAPER, encoding="utf-8") as f: text = f.read() patterns = [ (r"144[,]?853", "累计命中异常数据 144,853"), (r"476\s*次", "累计预警推送 476 次"), (r"10\s*条.*规则", "监测规则 10 条"), (r"19[,]?800", "惠州预警 19,800"), (r"3[,]?923", "佛山预警 3,923"), (r"8\s*个", "推广地市 8 个"), (r"7\s*场.*培训", "推广培训 7 场"), (r"100\s*人天", "供应商投入 100 人天"), (r"约\s*4\s*周", "端到端实际约 4 周"), (r"约\s*30\s*%", "标准化降本约 30%"), (r"表 6", "表 6 地市间对比"), (r"表 7", "表 7 实施路线图"), (r"表 8", "表 8 风险缓解矩阵"), (r"5\.0\s*倍", "惠州/佛山产出量差 5.0 倍"), (r"三阶段", "三阶段路线图"), (r"阶段门", "阶段门审核"), ] for pat, desc in patterns: if re.search(pat, text): ok(f"论文含 {desc}") else: fail(f"论文未找到 {desc}") def check_plots_exist(): print("[5] 校验图表文件存在") for name in ["pilot_run_data.png", "vendor_effort.png", "access_cost.png", "city_comparison.png", "city_comparison.csv", "roadmap_data.csv"]: path = os.path.join(PLOTS, name) if os.path.isfile(path) and os.path.getsize(path) > 0: ok(f"{name} 存在 ({os.path.getsize(path)} bytes)") else: fail(f"{name} 缺失或为空") def check_v4_content(): print("[6] 校验 v4.0 新增内容") with open(PAPER, encoding="utf-8") as f: text = f.read() checks_v4 = [ (r"3\.7\s*地市间对比", "3.7 地市间对比分析"), (r"5\.4\s*实施路线图", "5.4 实施路线图与风险缓解"), (r"稳定运行梯队", "稳定运行梯队"), (r"实际产出梯队", "实际产出梯队"), (r"推广接入梯队", "推广接入梯队"), (r"标准化筑基", "第一阶段:标准化筑基"), (r"评价闭环", "第二阶段:评价闭环"), (r"扩围深化", "第三阶段:扩围深化"), ] for pat, desc in checks_v4: if re.search(pat, text): ok(f"论文含 {desc}") else: fail(f"论文未找到 {desc}") nb_path = os.path.join(ROOT, "notebooks", "pilot_analysis.md") with open(nb_path, encoding="utf-8") as f: nb = f.read() for section in ["## 9. 地市间对比分析", "## 10. 实施路线图与风险缓解"]: if section in nb: ok(f"notebooks 含 {section}") else: fail(f"notebooks 缺少 {section}") def main(): print("=" * 60) print("数据一致性校验 — AI时代市场化运作机制研究") print("=" * 60) print() check_run_data() print() check_vendor_effort() print() check_access_cost() print() check_paper_numbers() print() check_plots_exist() print() check_v4_content() print() print("=" * 60) if errors: print(f"校验完成:{checks} 通过,{len(errors)} 失败") for e in errors: print(f" - {e}") sys.exit(1) else: print(f"校验完成:全部 {checks} 项通过,0 失败") sys.exit(0) if __name__ == "__main__": main()