"""F4-conf-01: extract printed values from two arXiv extracts, derive CI/SE, write out/results.json. Run from workspace root: ~/wp29/venv/bin/python out/analysis.py Reads only data/. No randomness.""" import json, re, os from decimal import Decimal, ROUND_HALF_UP ROOT = os.getcwd() DCR_ID, RI_ID = "arxiv-2609.27732v1", "arxiv-2606.14621v1" DCR_F = os.path.join(ROOT, "data/sources", DCR_ID + ".txt") RI_F = os.path.join(ROOT, "data/sources", RI_ID + ".txt") README_F = os.path.join(ROOT, "data/README.md") Z = 1.96 # normal critical value for two-sided 95% def lines_of(p): with open(p, encoding="utf-8") as f: return f.read().split("\n") def wc_l(p): with open(p, "rb") as f: return f.read().count(b"\n") def num(tok): """Printed token -> number preserving printed value (strip stars, %, $, commas, parentheses).""" t = tok.strip().replace("$", "").replace(",", "").strip("()").rstrip("*").strip() t = re.sub(r"\s*(pp|weeks|wks)$", "", t) v = float(t) return int(v) if re.fullmatch(r"-?\d+", t) else v def stars(tok): return len(tok.strip()) - len(tok.strip().rstrip("*")) def show(tag, ln_no, text): print(f"[{tag}] line {ln_no}: {text}") # ---------------- DCR source ---------------- D = lines_of(DCR_F) def table_block(L, title_prefix, end_prefix="Note:"): s = next(i for i, l in enumerate(L) if l.startswith(title_prefix)) e = next(i for i in range(s + 1, len(L)) if L[i].startswith(end_prefix)) return s, e def dcr_table(title_prefix): s, e = table_block(D, title_prefix) nonempty = [(i, D[i]) for i in range(s + 1, e) if D[i].strip()] hdr_i, hdr = nonempty[0] cols = [c.strip() for c in hdr.split("|")][1:] rows = {} k = 1 while k < len(nonempty): i, l = nonempty[k] cells = [c.strip() for c in l.split("|")] label, vals = cells[0], cells[1:] se = None if k + 1 < len(nonempty) and nonempty[k + 1][1].lstrip().startswith("|") and "(" in nonempty[k + 1][1]: j, sl = nonempty[k + 1] se = ([c.strip() for c in sl.split("|")][1:], j, sl) k += 1 rows[label] = (vals, i, l, se) k += 1 return cols, rows def dcr_cell(title, row, col): cols, rows = dcr_table(title) vals, i, l, se = rows[row] c = cols.index(col) show(f"{DCR_ID} {title.split(':')[0]}", i + 1, l) out = {"est_tok": vals[c], "est": num(vals[c]), "stars": stars(vals[c])} if se is not None: show(f"{DCR_ID} {title.split(':')[0]} SE", se[1] + 1, se[2]) out["se_tok"] = se[0][c] out["se"] = num(se[0][c]) return out T6, T7 = "Table 6:", "Table 7:" r = {} loc = {} def L(src, tab, row, col, extra=""): return f'{src} | Table {tab} | row "{row}" | column "{col}"' + extra a5 = dcr_cell(T6, "ATT", "5 km") a3 = dcr_cell(T6, "ATT", "3 km") ob5 = dcr_cell(T6, "Observations", "5 km") nar3 = dcr_cell(T7, "Narcotics", "3 km") fat1 = dcr_cell(T7, "Drug-related (fatal)", "1 km") hiv5 = dcr_cell(T7, "HIV", "5 km") r["dcr_att_5km"], r["dcr_att_5km_se"] = a5["est"], a5["se"] loc["dcr_att_5km"] = L(DCR_ID, 6, "ATT", "5 km") loc["dcr_att_5km_se"] = L(DCR_ID, 6, "ATT", "5 km", " (standard error in parentheses on the line below the coefficient row)") r["dcr_att_3km"], r["dcr_att_3km_se"] = a3["est"], a3["se"] loc["dcr_att_3km"] = L(DCR_ID, 6, "ATT", "3 km") loc["dcr_att_3km_se"] = L(DCR_ID, 6, "ATT", "3 km", " (standard error in parentheses on the line below the coefficient row)") r["dcr_obs_5km"] = int(ob5["est"]) loc["dcr_obs_5km"] = L(DCR_ID, 6, "Observations", "5 km") r["dcr_narcotics_att_3km"], r["dcr_narcotics_att_3km_se"] = nar3["est"], nar3["se"] loc["dcr_narcotics_att_3km"] = L(DCR_ID, 7, "Narcotics", "3 km") loc["dcr_narcotics_att_3km_se"] = L(DCR_ID, 7, "Narcotics", "3 km", " (standard error in parentheses on the line below)") r["dcr_fatal_att_1km"], r["dcr_fatal_att_1km_se"] = fat1["est"], fat1["se"] loc["dcr_fatal_att_1km"] = L(DCR_ID, 7, "Drug-related (fatal)", "1 km") loc["dcr_fatal_att_1km_se"] = L(DCR_ID, 7, "Drug-related (fatal)", "1 km", " (standard error in parentheses on the line below)") r["dcr_hiv_att_5km"], r["dcr_hiv_att_5km_se"] = hiv5["est"], hiv5["se"] loc["dcr_hiv_att_5km"] = L(DCR_ID, 7, "HIV", "5 km") loc["dcr_hiv_att_5km_se"] = L(DCR_ID, 7, "HIV", "5 km", " (standard error in parentheses on the line below)") # exact p-value: search the extract for any printed p-value (p = / p= followed by digits) pv_hits = [(i + 1, l) for i, l in enumerate(D) if re.search(r"\bp\s*=\s*0?\.\d", l)] print(f"[{DCR_ID}] lines with an exact printed p-value: {len(pv_hits)}") r["dcr_hiv_att_5km_p_value"] = None loc["dcr_hiv_att_5km_p_value"] = (f"not reported in {DCR_ID} (Table 7 prints only the coefficient, the SE in " "parentheses and significance stars; no exact p-value is printed anywhere in the extract)") r["dcr_hiv_att_5km_significant_10pct"] = hiv5["stars"] >= 1 print(f"[{DCR_ID}] HIV 5 km token '{hiv5['est_tok']}' has {hiv5['stars']} star(s) -> significant at 10%: {hiv5['stars'] >= 1}") def r3(x): return float(Decimal(repr(x)).quantize(Decimal("0.001"), rounding=ROUND_HALF_UP)) lo_raw = a5["est"] - Z * a5["se"] hi_raw = a5["est"] + Z * a5["se"] r["dcr_att_5km_ci_low"], r["dcr_att_5km_ci_high"] = r3(lo_raw), r3(hi_raw) loc["dcr_att_5km_ci_low"] = L(DCR_ID, 6, "ATT", "5 km", " — derived: ATT - 1.96*SE from the printed Table 6 values") loc["dcr_att_5km_ci_high"] = L(DCR_ID, 6, "ATT", "5 km", " — derived: ATT + 1.96*SE from the printed Table 6 values") # ---------------- RESEA source ---------------- R = lines_of(RI_F) def ri_table(header_start, after_marker): s = next(i for i, l in enumerate(R) if l.startswith(after_marker)) h = next(i for i in range(s + 1, len(R)) if R[i].startswith(header_start)) cols = [c.strip() for c in R[h].split("|")] rows = {} i = h + 1 while i < len(R) and R[i].strip(): cells = [c.strip() for c in R[i].split("|")] rows[cells[0]] = (cells, i) i += 1 return cols, rows def parse_effect(cell): parts = [p.strip() for p in cell.split(";")] est = num(parts[0]) ci = re.search(r"\[(.*)\]", parts[2]).group(1) # CI bounds separated by ", " (thousands separators have no space) lo, hi = [x.strip() for x in ci.split(", ")] return {"est": est, "p": parts[1], "lo": num(lo), "hi": num(hi), "ci_tok": ci} cols2, rows2 = ri_table("Outcome | Treatment", "[Table 2") ATE_COL = "Average treatment effect estimate" ci_ = cols2.index(ATE_COL) cc = cols2.index("Control") def ri_row(name): cells, i = rows2[name] show(f"{RI_ID} Table 2", i + 1, R[i]) return cells w = ri_row("Wage") we = parse_effect(w[ci_]) r["ri_wage_ate"], r["ri_wage_ate_ci_low"], r["ri_wage_ate_ci_high"] = we["est"], we["lo"], we["hi"] loc["ri_wage_ate"] = L(RI_ID, 2, "Wage", ATE_COL, " (point estimate)") loc["ri_wage_ate_ci_low"] = L(RI_ID, 2, "Wage", ATE_COL, " (95% CI lower bound)") loc["ri_wage_ate_ci_high"] = L(RI_ID, 2, "Wage", ATE_COL, " (95% CI upper bound)") r["ri_wage_ate_se_reported"] = ("SE" in w[ci_]) or ("(" in w[ci_]) print(f"[{RI_ID}] wage ATE cell contains a standard error: {r['ri_wage_ate_se_reported']}") r["ri_wage_ate_se_derived"] = round((we["hi"] - we["lo"]) / (2 * Z), 2) loc["ri_wage_ate_se_derived"] = L(RI_ID, 2, "Wage", ATE_COL, " — derived: (CI upper - CI lower)/(2*1.96) from the printed Table 2 95% CI") ctrl_tok = w[cc].split("(")[0].strip() r["ri_control_mean_wage"] = num(ctrl_tok) loc["ri_control_mean_wage"] = L(RI_ID, 2, "Wage", "Control", " (mean; SD in parentheses not used)") re_ = parse_effect(ri_row("Reemployment")[ci_]) r["ri_reemployment_ate_pp"] = re_["est"] loc["ri_reemployment_ate_pp"] = L(RI_ID, 2, "Reemployment", ATE_COL, " (point estimate, pp)") wu = parse_effect(ri_row("Weeks on UI")[ci_]) r["ri_weeks_ui_ate"], r["ri_weeks_ui_ate_ci_low"], r["ri_weeks_ui_ate_ci_high"] = wu["est"], wu["lo"], wu["hi"] loc["ri_weeks_ui_ate"] = L(RI_ID, 2, "Weeks on UI", ATE_COL, " (point estimate)") loc["ri_weeks_ui_ate_ci_low"] = L(RI_ID, 2, "Weeks on UI", ATE_COL, " (95% CI lower bound)") loc["ri_weeks_ui_ate_ci_high"] = L(RI_ID, 2, "Weeks on UI", ATE_COL, " (95% CI upper bound)") # Cross-check with Table 3 (duplicate of Table 2 ATE column) — informational only cols3, rows3 = ri_table("Outcome | Difference in means", "[Table 3") t3_w = parse_effect(rows3["Wage"][0][cols3.index("Difference in means")]) t3_match = (t3_w["est"], t3_w["lo"], t3_w["hi"]) == (we["est"], we["lo"], we["hi"]) print(f"[{RI_ID}] Table 3 'Difference in means' Wage equals Table 2 ATE Wage: {t3_match}") # ---------------- Cross-table checks feeding data_issues ---------------- cols_a19, rows_a19 = dcr_table("Table A19:") a19 = rows_a19["ATT"] a19_se = num(a19[3][0][cols_a19.index("CS-DR")]) a19_est = num(a19[0][cols_a19.index("CS-DR")]) print(f"[{DCR_ID}] Table A19 CS-DR 5 km: ATT {a19_est}, SE {a19_se}; Table 6 5 km: ATT {a5['est']}, SE {a5['se']}") _, rows6 = dcr_table(T6) obs = [num(x) for x in rows6["Observations"][0]] treated = [num(x) for x in rows6["Treated regions"][0]] ctrl6 = [num(x) for x in rows6["Control regions"][0]] implied_ctrl = [o / 25 - t for o, t in zip(obs, treated)] print(f"[{DCR_ID}] Observations/25 - treated = {implied_ctrl}; printed control regions = {ctrl6}") data_issues = [ {"issue": f"{DCR_ID}: the 5 km CS-DR ATT -0.431 has SE (0.210) in Table 6 but SE ({a19_se}) in Table A19 (same estimator, buffer, window, and 17,250 observations).", "handling": "Used the Table 6 value (0.210) as the key definition names Table 6; the derived 95% CI uses 0.210. Flagged as an internal inconsistency of the source."}, {"issue": f"{DCR_ID}: control-region count is 608 in Tables 6/7 and the figure notes but 640 in the Table A19 note; the Table 6 Observations (16,175 / 16,875 / 17,250) equal (640 + treated regions) x 25 in every column, not (608 + treated) x 25.", "handling": "Observations reported as printed (17250); inconsistency flagged only (the 25-period panel length is an inference, not stated in the extract)."}, {"issue": f"{DCR_ID}: prose differs from tables — the abstract says fatal outcomes fall by 35% while the Table 7 5 km fatal ATT -0.354 corresponds to about -30% (Section 6.1 says ~30%); the text calls HIV estimates 'close to zero' while the 5 km HIV ATT is -0.340 (not starred).", "handling": "All keys taken from the tables named in TASK.md; prose not used."}, {"issue": f"{RI_ID}: Section 3 text reports the weeks-on-UI ATE CI as magnitudes '[1.75 weeks, 2.24 weeks]' whereas Table 2 prints [-2.24, -1.75].", "handling": "Used Table 2 values with printed signs (lower bound -2.24 first)."}, {"issue": f"{RI_ID}: rendering defect 'p ¡ 0.001' (for p < 0.001) in the Section 3 short-term weeks-on-UI sentence; Tables 2-3 were re-set by the extract editor into one row per outcome.", "handling": "Defective text not used; re-set table values used as declared verbatim; Table 3 column 1 Wage ATE cross-checked equal to Table 2."}, ] n_input_rows = {"README.md": wc_l(README_F), DCR_ID + ".txt": wc_l(DCR_F), RI_ID + ".txt": wc_l(RI_F)} results = {**r, "locators": loc, "derivation_notes": { "dcr_att_5km_ci": ("Normal-approximation (Wald) 95% CI: ATT +/- 1.96 x SE using printed Table 6 values ATT = -0.431, SE = 0.210 " f"(unrounded {lo_raw:.4f}, {hi_raw:.4f}; reported rounded to 3 decimals as in the table). Assumes asymptotic normality " "of the CS-DR estimator and the clustered SE; not printed by the source. Using Table A19's SE 0.218 instead would give a wider interval."), "ri_wage_ate_se": ("SE = (upper - lower)/(2 x 1.96) from the printed Table 2 95% CI [166, 2,141] = 1975/3.92 " f"(unrounded {(we['hi'] - we['lo']) / (2 * Z):.4f}; reported to 2 decimals). Assumes the printed CI is a symmetric normal-approximation " "Wald interval (z = 1.96; with N = 23,549 a t critical value is practically identical); the CI bounds are rounded to whole dollars, so the " "derived SE is accurate only to about +/-0.3 USD. Source prints no SE.")}, "task_id": "F4-conf-01", "feasibility": "complete", "feasibility_reason": None, "data_issues": data_issues, "n_input_rows": n_input_rows, "n_analysed": 22, "random_seed": None, } os.makedirs(os.path.join(ROOT, "out"), exist_ok=True) with open(os.path.join(ROOT, "out/results.json"), "w", encoding="utf-8") as f: json.dump(results, f, indent=2, ensure_ascii=False) print("wrote out/results.json") for k in r: print(f" {k} = {r[k]}")