SPB Git

spb/wp29_uqo

Public
8.2 KB · 148 lines python
1"""F4-conf-03: harmonise five printed effect sizes. Reads only data/, writes out/results.json.2No randomness. Every printed input is located by regex in the extract and its line is printed."""3import json, math, os, re, sys45ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))6D = os.path.join(ROOT, "data")7S = os.path.join(D, "sources")89def lines_of(fn):10    with open(os.path.join(S, fn + ".txt"), encoding="utf-8") as f:11        return f.read().split("\n")1213def find(src, pattern):14    """Return (line_no, line, match) for the unique line matching pattern."""15    L = lines_of(src)16    hits = [(i + 1, l, re.search(pattern, l)) for i, l in enumerate(L) if re.search(pattern, l)]17    if len(hits) != 1:18        sys.exit(f"pattern {pattern!r} matched {len(hits)} lines in {src}")19    n, l, m = hits[0]20    print(f"[{src} L{n}] {l.strip()}")21    return n, l, m2223def cell(line, idx):24    return [c.strip() for c in line.split("|")][idx]2526def num(s):27    return float(re.sub(r"[^\d.\-]", "", s))2829effects = json.load(open(os.path.join(D, "effects.json"), encoding="utf-8"))30assert [e["id"] for e in effects] == ["E1", "E2", "E3", "E4", "E5"]31H = {}3233# E1: Table 6, ATT row, 5 km column (4th cell)34src = "arxiv-2609.27732v1"35n, l, _ = find(src, r"^ATT \| -0\.399")36b1_txt = cell(l, 3)                      # '-0.431**'37b1 = num(b1_txt.replace("*", ""))38n_pc, l_pc, _ = find(src, r"^% change \| -32\.9%")39printed_pc = cell(l_pc, 3)40v1 = 100 * (math.exp(b1) - 1)41H["E1"] = dict(value=v1, unit="percent (relative change vs counterfactual rate)",42    transformation=f"100*(exp(b)-1) with b={b1} -> 100*(exp({b1})-1) = {v1:.4f}% (printed check: {printed_pc})",43    harmonisable=True, missing_quantity=None,44    locator=f"{src} | Table 6 | row 'ATT' (line {n}) | column '5 km' = {b1_txt}; cross-check row '% change' (line {n_pc}) | column '5 km' = {printed_pc}")4546# E2: Table 2, Reemployment row47src = "arxiv-2606.14621v1"48n, l, _ = find(src, r"^Reemployment \| 75\.2%")49ctrl_txt = cell(l, 2); ate_txt = cell(l, 4).split(";")[0]50p = num(ctrl_txt); d = num(ate_txt)51v2 = 100 * d / p52H["E2"] = dict(value=v2, unit="percent (relative change vs control-group mean)",53    transformation=f"100*Delta/p with Delta={d} pp, p={p}% -> 100*{d}/{p} = {v2:.4f}%",54    harmonisable=True, missing_quantity=None,55    locator=f"{src} | Table 2 | row 'Reemployment' (line {n}) | column 'Average treatment effect estimate' = {ate_txt}; column 'Control' = {ctrl_txt}")5657# E3: Table 2, elasticity row and '% MW changes' row58src = "arxiv-2505.04555v3"59n_e, l_e, _ = find(src, r"^Employment elasticity w\.r\.t MW \|")60eta = num(cell(l_e, 1))61n_g, l_g, _ = find(src, r"^% MW changes \|")62g = num(cell(l_g, 1))63find(src, r"about 4% minimum wage increase")64v3 = 100 * eta * g65H["E3"] = dict(value=v3, unit="percent (relative change in employment)",66    transformation=f"100*eta*g with eta={cell(l_e,1)}, g={cell(l_g,1)} (proportional MW change) -> 100*({eta})*({g}) = {v3:.4f}%",67    harmonisable=True, missing_quantity=None,68    locator=f"{src} | Table 2 | row 'Employment elasticity w.r.t MW' (line {n_e}) | column 'Estimate (SE)' = {cell(l_e,1)}; row '% MW changes' (line {n_g}) | column 'Estimate (SE)' = {cell(l_g,1)}")6970# E4: Table 7, fatal row, 5 km column; absolute change needs the counterfactual level71src = "arxiv-2609.27732v1"72n, l, _ = find(src, r"^Drug-related \(fatal\) \|")73b4_txt = cell(l, 3); b4 = num(b4_txt.replace("*", ""))74rel4 = math.exp(b4) - 175H["E4"] = dict(value=None, unit="hospitalisations per 100,000 inhabitants per year",76    transformation=(f"Absolute change = R0*(exp(b)-1) with b={b4} -> R0*({rel4:.4f}); "77                    "R0 (counterfactual/baseline fatal drug-related hospitalisation rate per 100,000 in treated 5 km regions) is not printed in the extract, so the value cannot be computed"),78    harmonisable=False,79    missing_quantity="baseline (counterfactual, e.g. pre-opening or control-group mean) level of the fatal drug-related hospitalisation rate per 100,000 inhabitants per year for the treated 5 km regions",80    locator=f"{src} | Table 7 | row 'Drug-related (fatal)' (line {n}) | column '5 km' = {b4_txt}")8182# E5: Table 3, Treatment row, column (4)83src = "arxiv-2510.03658v1"84n, l, _ = find(src, r"^Treatment \| -0\.0911")85b5_txt = cell(l, 4); b5 = num(b5_txt)86v5 = 100 * b587H["E5"] = dict(value=v5, unit="percentage points",88    transformation=f"100*beta with beta={b5_txt} (0-1 scale) -> 100*({b5}) = {v5:.2f} pp",89    harmonisable=True, missing_quantity=None,90    locator=f"{src} | Table 3 | row 'Treatment' (line {n}) | column '(4)' = {b5_txt}")9192# ---- internal-consistency evidence for data_issues (all from the extracts) ----93src = "arxiv-2609.27732v1"94find(src, r"fatal outcomes, which fall by 35%")95find(src, r"by approximately 30% at 5 km")96n_a19, l_a19, _ = find(src, r"^\| \(0\.218\)")97n_se6, l_se6, _ = find(src, r"^\| \(0\.412\)")98find(src, r"never-treated comparison regions \(640 regions\)")99src = "arxiv-2510.03658v1"100find(src, r"by 0\.0855 \(a 18\.79 percentage change\)")101find(src, r"The result in table 2 suggests")102src = "arxiv-2606.14621v1"103find(src, r"increased reemployment by 1\.5 percentage points")104105fatal_rel = 100 * rel4106data_issues = [107    {"issue": f"arxiv-2609.27732v1: abstract says fatal outcomes 'fall by 35%', whereas Section 6.1 says 'approximately 30% at 5 km'; Table 7 5 km fatal ATT {b4} implies 100*(exp({b4})-1) = {fatal_rel:.1f}%, consistent with ~30% not 35%.",108     "handling": "E4 is based on the Table 7 coefficient only; the abstract figure is not used. Noted as an internal inconsistency of the source."},109    {"issue": "arxiv-2609.27732v1: the CS-DR 5 km ATT -0.431 has SE (0.210) in Table 6 but (0.218) in Table A19; Table 6 note says 608 control regions while the Table A19 note says 640 never-treated comparison regions (Table A19 body says 608).",110     "handling": "Point estimate identical in both tables, so E1 is unaffected; no SE is propagated. Recorded only."},111    {"issue": f"arxiv-2505.04555v3: Table 2 prints '% MW changes' = {g} (4.7%) while the table note describes the 2023 change as 'about 4% minimum wage increase'.",112     "handling": f"E3 uses the printed table value g={g}, as the target definition asks for the size printed in the same table; with 0.04 the implied change would be {100*eta*0.04:.4f}% (reported for transparency only)."},113    {"issue": "arxiv-2510.03658v1: the abstract and Section 4.1 describe -0.0855 as 'a 18.79 percentage change' (a relative change computed against a mean of 0.455), and Section 4.1 attributes it to 'table 2', which is not in the extract; Table 3 relative change (-0.3685) is computed against the overall dependent-variable mean (0.261), not a counterfactual level.",114     "handling": "E5 only requires rescaling the Table 3 column (4) LPM coefficient to percentage points (exact); the relative-change figures are not used."},115    {"issue": "arxiv-2606.14621v1: the abstract rounds the reemployment ATE to 1.5 pp; Table 2 prints 1.47 pp. The ATE is block-adjusted while the 75.0% control mean is the raw (unadjusted) aggregate control-group mean.",116     "handling": "E2 uses the Table 2 values (1.47 pp, 75.0%) exactly as the target definition (percent of the control-group mean) specifies; noted as a caveat."},117]118119def nlines(path):120    with open(path, "rb") as f:121        return f.read().count(b"\n")122123n_input_rows = {"effects.json": len(effects), "README.md": nlines(os.path.join(D, "README.md"))}124for fn in sorted(os.listdir(S)):125    n_input_rows[fn] = nlines(os.path.join(S, fn))126127n_h = sum(1 for v in H.values() if v["value"] is not None)128n_nh = sum(1 for v in H.values() if v["harmonisable"] is False)129res = {130    "task_id": "F4-conf-03",131    "harmonised": H,132    "n_harmonised": n_h,133    "n_not_harmonisable": n_nh,134    "feasibility": "complete",135    "feasibility_reason": None,136    "data_issues": data_issues,137    "n_input_rows": n_input_rows,138    "n_analysed": len(effects),139    "random_seed": None,140}141assert n_h + n_nh == len(effects)142os.makedirs(os.path.join(ROOT, "out"), exist_ok=True)143with open(os.path.join(ROOT, "out", "results.json"), "w", encoding="utf-8") as f:144    json.dump(res, f, indent=2, ensure_ascii=False)145for k, v in H.items():146    print(k, v["value"], v["unit"], "| harmonisable:", v["harmonisable"])147print("n_harmonised", n_h, "n_not_harmonisable", n_nh, "feasibility complete")148
Files · main
Menu

Theme