"""Replication script for F4-conf-02: verdicts on six assertions. Run from workspace root: ~/wp29/venv/bin/python out/analysis.py Parses the printed values from the source extracts, applies the task's conventions (pct = 100*(exp(b)-1); 95% CI = b +/- 1.96*SE, robustness with 2.0) and writes out/results.json.""" import json, math, re, os D = "data" DCR = "arxiv-2609.27732v1" RES = "arxiv-2606.14621v1" def lines(p): with open(p, encoding="utf-8") as f: return f.read().split("\n") assertions = json.load(open(os.path.join(D, "assertions.json"), encoding="utf-8")) dcr = lines(os.path.join(D, "sources", DCR + ".txt")) res = lines(os.path.join(D, "sources", RES + ".txt")) def wc(p): with open(p, "rb") as f: return f.read().count(b"\n") def find(ls, pat, start=0): for i in range(start, len(ls)): if re.search(pat, ls[i]): return i raise ValueError(pat) def show(tag, ls, i): print(f"[{tag} line {i+1}] {ls[i]}") def cells(s): return [c.strip() for c in s.split("|")] def num(s): return float(re.sub(r"[^\d\.\-]", "", s.replace("−", "-"))) def sig(s): return s.count("*") print("=== Source lines relied on ===") # Table 6 t6 = find(dcr, r"^Table 6:") i_att = find(dcr, r"^ATT \|", t6); i_se = i_att + 2; i_pct = find(dcr, r"^% change \|", t6) for i in (i_att, i_se, i_pct): show(DCR, dcr, i) att6 = cells(dcr[i_att])[1:]; se6 = cells(dcr[i_se])[1:]; pct6 = cells(dcr[i_pct])[1:] cols = ["1 km", "3 km", "5 km"] T6 = {c: dict(b=num(a), stars=sig(a), se=num(s), pct=p) for c, a, s, p in zip(cols, att6, se6, pct6)} i_txt = find(dcr, r"5 km specification, yields an ATT"); show(DCR, dcr, i_txt); show(DCR, dcr, i_txt + 1) # Table 7 t7 = find(dcr, r"^Table 7:") T7 = {} for row in ["Mental/Behavioral", "Hepatitis", "HIV", "Narcotics", r"Drug-related \(fatal\)"]: i = find(dcr, "^" + row + r" \|", t7) show(DCR, dcr, i); show(DCR, dcr, i + 2) c = cells(dcr[i]); s = cells(dcr[i + 2])[1:] T7[c[0]] = {col: dict(b=num(a), stars=sig(a), se=num(se)) for col, a, se in zip(cols, c[1:], s)} # Table A19 ta = find(dcr, r"^Table A19:") ia = find(dcr, r"^ATT \|", ta); show(DCR, dcr, ia); show(DCR, dcr, ia + 2) ina = find(dcr, r"never-treated comparison regions \(640", ta); show(DCR, dcr, ina) a19_se_csdr = num(cells(dcr[ia + 2])[1]) # Sex / gender heterogeneity search in DCR extract sex_hits = [i for i, l in enumerate(dcr) if re.search(r"\b(men|women|male|female|sex|gender)\b", l, re.I)] print(f"[{DCR}] lines mentioning men/women/male/female/sex/gender: {len(sex_hits)}") # RESEA Table 2 iw = find(res, r"^Weeks on UI \|"); show(RES, res, iw) c = cells(res[iw]) ctrl_mean = num(c[2].split("weeks")[0]) m = re.search(r"(-?[\d\.]+) weeks; p<0\.001; 95% CI: \[(-?[\d\.]+), (-?[\d\.]+)\]", c[4]) ate, lo, hi = float(m.group(1)), float(m.group(2)), float(m.group(3)) it = find(res, r"11\.5% decrease"); show(RES, res, it) print("\n=== Checks ===") def pct(b): return 100 * (math.exp(b) - 1) def ci(b, se, z): return (b - z * se, b + z * se) checks = {} # A1 a = T6["5 km"] checks["A1"] = dict(b=a["b"], se=a["se"], stars=a["stars"], pct_printed=a["pct"], pct_calc=round(pct(a["b"]), 2), z=round(abs(a["b"]) / a["se"], 3), z_A19_se=round(abs(a["b"]) / a19_se_csdr, 3)) ok1 = (a["b"] == -0.431 and a["se"] == 0.210 and a["stars"] == 2 and round(pct(a["b"]), 1) == -35.0) # A2 h = T7["HIV"]["1 km"]; checks["A2"] = dict(b=h["b"], se=h["se"]) ok2 = h["b"] < 0 # A3 b3 = T6["3 km"]; checks["A3"] = dict(b=b3["b"], pct_printed=b3["pct"], pct_calc=round(pct(b3["b"]), 2)) ok3 = abs(pct(b3["b"]) - (-28.3)) < 0.05 # A4 hp = T7["Hepatitis"]["5 km"] ci196 = ci(hp["b"], hp["se"], 1.96); ci200 = ci(hp["b"], hp["se"], 2.0); ci1 = ci(hp["b"], hp["se"], 1.0) checks["A4"] = dict(b=hp["b"], se=hp["se"], stars=hp["stars"], ci_196=[round(x, 3) for x in ci196], ci_200=[round(x, 3) for x in ci200], ci_1se=[round(x, 3) for x in ci1]) ok4 = ci196[1] < 0 and abs(ci196[0] + 0.66) < 0.02 and abs(ci196[1] + 0.16) < 0.02 # A5 checks["A5"] = dict(sex_mentions=len(sex_hits)) # A6 checks["A6"] = dict(ate=ate, ci=[lo, hi], control_mean=ctrl_mean, pct_calc=round(100 * ate / ctrl_mean, 2)) ok6 = ate == -1.99 and (lo, hi) == (-2.24, -1.75) and ctrl_mean == 17.3 and round(100 * ate / ctrl_mean, 1) == -11.5 for k, v in checks.items(): print(k, v) V = lambda ok: "supported" if ok else "not_supported" verdicts = {"A1": V(ok1), "A2": V(ok2), "A3": V(ok3), "A4": V(ok4), "A5": "not_assessable" if not sex_hits else None, "A6": V(ok6)} locators = { "A1": f'{DCR} | Table 6 | row "ATT" | column "5 km"', "A2": f'{DCR} | Table 7 | row "HIV" | column "1 km"', "A3": f'{DCR} | Table 6 | row "% change" | column "3 km"', "A4": f'{DCR} | Table 7 | row "Hepatitis" | column "5 km"', "A5": f"not reported in {DCR}", "A6": f'{RES} | Table 2 | row "Weeks on UI" | column "Average treatment effect estimate"', } reasons = { "A1": f"Table 6 prints ATT -0.431** (SE 0.210) at 5 km with p<0.05 and '% change' -35.0%, and 100*(exp(-0.431)-1) = {checks['A1']['pct_calc']}%, so sign, magnitude, SE, significance and percentage all match.", "A2": f"The 1 km HIV ATT in Table 7 is printed as +0.063 (SE 0.801), not -0.063, so the asserted negative sign and the claimed fall in HIV hospitalisations are contradicted.", "A3": f"A log-point ATT of -0.283 implies 100*(exp(-0.283)-1) = {checks['A3']['pct_calc']}%, matching the printed '% change' of -24.7%, not 28.3% (the assertion equates log points with percent).", "A4": f"With ATT -0.406 and SE 0.250 the 95% CI is [{ci196[0]:.3f}, {ci196[1]:.3f}] (±1.96·SE) and includes zero (estimate is unstarred); the asserted [-0.66, -0.16] is only ±1 SE.", "A5": "The extract reports no estimates by sex (no men/women/gender subgroup appears in the abstract, Section 6, Tables 6-7 or Table A19), so the comparison cannot be confirmed or refuted.", "A6": f"Table 2 prints the ATE on weeks on UI as -1.99 weeks (p<0.001; 95% CI [-2.24, -1.75]) with control mean 17.3 weeks, and 1.99/17.3 = {abs(checks['A6']['pct_calc'])}%, matching the 11.5% stated in the results text.", } data_issues = [ {"issue": "arxiv-2609.27732v1: the same CS-DR 5 km ATT (-0.431**) has SE 0.210 in Table 6 but SE 0.218 in Appendix Table A19 (column 'CS-DR').", "handling": "A1 judged against Table 6, which the assertion paraphrases; the verdict is unaffected because |t| = 2.052 (SE 0.210) or 1.977 (SE 0.218) both exceed 1.96 and both tables print ** (p<0.05)."}, {"issue": "arxiv-2609.27732v1: Table A19 note says 640 never-treated comparison regions while its own body and Tables 6-7 report 608 control regions.", "handling": "Recorded only; no assertion depends on the control-region count."}, {"issue": "arxiv-2609.27732v1: abstract says fatal outcomes 'fall by 35%', but Table 7 5 km fatal ATT -0.354 implies -29.8%, which Section 6.1 reports as 'approximately 30%'.", "handling": "Recorded only; no assertion concerns fatal hospitalisations. Tables preferred over prose where needed."}, {"issue": "arxiv-2609.27732v1: Table A19 '% change' row prints -30.3% (Synthetic DiD, -0.362) and -13.9% (Stacked DiD, -0.149) whereas 100*(exp(b)-1) gives -30.37% and -13.84%; differences of 0.1 pp, likely rounding from unrounded coefficients.", "handling": "Recorded only; not decisive for any assertion."}, {"issue": "arxiv-2609.27732v1: the text describes HIV estimates as 'close to zero' although the 5 km HIV ATT is -0.340 (SE 0.294); they are insignificant but not small in magnitude.", "handling": "Recorded only; A2 is decided from the printed 1 km coefficient's sign."}, {"issue": "arxiv-2606.14621v1: results text reports weeks-on-UI CIs as magnitudes ([1.75 weeks, 2.24 weeks], [1.55, 1.99]) while Table 2 prints signed intervals [-2.24, -1.75]; and 'p ¡ 0.001' is an HTML rendering defect for 'p < 0.001'.", "handling": "Treated as equivalent descriptions of a reduction; A6 judged against the signed Table 2 values."}, {"issue": "arxiv-2606.14621v1: Tables 2 and 3 were re-set by the extract editor (one row per outcome) per the extract header.", "handling": "Values used as printed; Table 3 'Difference in means' column duplicates Table 2 ATE column, consistent (-1.99; [-2.24, -1.75])."}, ] counts = {k: sum(1 for v in verdicts.values() if v == k) for k in ["supported", "not_supported", "not_assessable"]} results = { "task_id": "F4-conf-02", "verdicts": verdicts, "locators": locators, "reasons": reasons, "n_supported": counts["supported"], "n_not_supported": counts["not_supported"], "n_not_assessable": counts["not_assessable"], "feasibility": "complete", "feasibility_reason": None, "data_issues": data_issues, "n_input_rows": { "assertions.json": len(assertions), "README.md": wc(os.path.join(D, "README.md")), f"sources/{DCR}.txt": wc(os.path.join(D, "sources", DCR + ".txt")), f"sources/{RES}.txt": wc(os.path.join(D, "sources", RES + ".txt")), }, "n_analysed": len(assertions), "random_seed": None, "checks": checks, } os.makedirs("out", exist_ok=True) with open("out/results.json", "w", encoding="utf-8") as f: json.dump(results, f, indent=2, ensure_ascii=False) print("\nVerdicts:", verdicts) print("Counts:", counts)