SPB Git

spb/wp29_uqo

Public
8.8 KB · 156 lines python
1"""F4-conf-03: harmonise five effect sizes from printed source extracts.2Run from workspace root: ~/wp29/venv/bin/python out/analysis.py3Reads only data/; writes out/results.json. Deterministic, no randomness."""4import json, math, os, re56DATA = "data"7SRC = os.path.join(DATA, "sources")8910def lines_of(fname):11    with open(os.path.join(SRC, fname), encoding="utf-8") as f:12        return f.read().split("\n")131415def find_line(fname, pattern):16    """Return (line_no, text) of the unique line matching regex pattern; print it."""17    L = lines_of(fname)18    hits = [(i + 1, l) for i, l in enumerate(L) if re.search(pattern, l)]19    assert len(hits) == 1, (fname, pattern, hits)20    n, t = hits[0]21    print(f"[{fname}:{n}] {t.strip()[:200]}")22    return n, t232425def cells(text):26    return [c.strip() for c in text.split("|")]272829def num(s):30    m = re.search(r"-?\d[\d,]*\.?\d*", s)31    return float(m.group(0).replace(",", ""))323334def wc_l(path):35    with open(path, "rb") as f:36        return f.read().count(b"\n")373839effects = json.load(open(os.path.join(DATA, "effects.json"), encoding="utf-8"))40print(f"effects.json: {len(effects)} items")41H = {}4243# ---- E1: log-point ATT -> percent change ----44f = "arxiv-2609.27732v1.txt"45n_hdr, hdr = find_line(f, r"^\| 1 km \| 3 km \| 5 km$")46n_att, att = find_line(f, r"^ATT \| -0\.399")47n_pct, pct = find_line(f, r"^% change \| -32\.9%")48col = cells(hdr).index("5 km")49b1_s = cells(att)[col].rstrip("*")50b1 = float(b1_s)51v1 = 100 * (math.exp(b1) - 1)52printed_pct = cells(pct)[col]53print(f"E1: b={b1_s}; 100*(exp(b)-1)={v1:.4f}%; printed '% change' = {printed_pct}")54H["E1"] = dict(55    value=round(v1, 2), unit="percent (relative change vs counterfactual rate)",56    transformation=f"100*(exp({b1_s}) - 1) = {v1:.4f}% (exact conversion of a log-point coefficient; matches the printed '% change' {printed_pct})",57    harmonisable=True, missing_quantity=None,58    locator=f"arxiv-2609.27732v1 | Table 6 | row 'ATT' | column '5 km' (extract line {n_att}); cross-check: row '% change', column '5 km' (line {n_pct})")5960# ---- E2: pp effect -> percent of control mean ----61f = "arxiv-2606.14621v1.txt"62n_h2, h2 = find_line(f, r"^Outcome \| Treatment \| Control \|")63n_r2, r2 = find_line(f, r"^Reemployment \| 75\.2%")64hc = cells(h2); rc = cells(r2)65ate_s = rc[hc.index("Average treatment effect estimate")].split(";")[0].replace(" pp", "").strip()66ctl_s = rc[hc.index("Control")].replace("%", "").strip()67d2, p2 = float(ate_s), float(ctl_s)68v2 = 100 * d2 / p269print(f"E2: Delta={ate_s} pp, control mean={ctl_s}% -> {v2:.4f}%")70H["E2"] = dict(71    value=round(v2, 2), unit="percent (relative to the control-group mean reemployment rate)",72    transformation=f"100*{ate_s} pp / {ctl_s}% = {v2:.4f}% (exact given the definition; denominator = raw control-group mean)",73    harmonisable=True, missing_quantity=None,74    locator=f"arxiv-2606.14621v1 | Table 2 | row 'Reemployment' | column 'Average treatment effect estimate' (1.47 pp) and column 'Control' (75.0%) (extract line {n_r2})")7576# ---- E3: elasticity x %MW change -> percent change in employment ----77f = "arxiv-2505.04555v3.txt"78n_e3, e3 = find_line(f, r"^Employment elasticity w\.r\.t MW \|")79n_g3, g3 = find_line(f, r"^% MW changes \|")80find_line(f, r"about 4% minimum wage increase")81eta_s = cells(e3)[1]; g_s = cells(g3)[1]82eta, g = float(eta_s), float(g_s)83v3 = 100 * eta * g84print(f"E3: eta={eta_s}, g={g_s} -> {v3:.4f}%")85H["E3"] = dict(86    value=round(v3, 2), unit="percent (relative change in employment)",87    transformation=f"100*({eta_s})*({g_s}) = {v3:.4f}% (first-order/linear approximation; g = printed '% MW changes' = 0.047, i.e. 4.7%)",88    harmonisable=True, missing_quantity=None,89    locator=f"arxiv-2505.04555v3 | Table 2 | row 'Employment elasticity w.r.t MW' | column 'Estimate (SE)' (line {n_e3}); row '% MW changes' | column 'Estimate (SE)' (line {n_g3})")9091# ---- E4: log-point ATT -> absolute change per 100,000: needs baseline level ----92f = "arxiv-2609.27732v1.txt"93n_e4, e4 = find_line(f, r"^Drug-related \(fatal\) \|")94b4_s = cells(e4)[3].rstrip("*")95b4 = float(b4_s)96rel4 = 100 * (math.exp(b4) - 1)97print(f"E4: b={b4_s}; relative change would be {rel4:.4f}%; no level of the fatal rate is printed in the extract")98# check that no baseline/mean level of the fatal rate is printed99L = lines_of(f)100lvl = [l for l in L if re.search(r"(mean|baseline|average|pre-treatment).{0,60}(rate|per 100,000)", l, re.I)101       and re.search(r"\d+(\.\d+)? (hospitali[sz]ations )?per 100,000", l)]102print(f"E4: lines printing a numeric level per 100,000: {len(lvl)}")103H["E4"] = dict(104    value=None, unit="hospitalisations per 100,000 inhabitants per year",105    transformation=(f"absolute change = R0*(exp({b4_s}) - 1), where R0 is the counterfactual (pre-treatment/baseline) fatal drug-related "106                    f"hospitalisation rate per 100,000 in the treated (5 km) regions; the relative part is exp({b4_s})-1 = {rel4/100:.4f} "107                    f"({rel4:.2f}%), but R0 is not printed in the extract, so the target cannot be reached"),108    harmonisable=False,109    missing_quantity="baseline (counterfactual / pre-treatment) level of the fatal drug-related hospitalisation rate per 100,000 inhabitants per year in the treated 5 km regions (not printed in the extract)",110    locator=f"arxiv-2609.27732v1 | Table 7 | row 'Drug-related (fatal)' | column '5 km' (extract line {n_e4})")111112# ---- E5: LPM coefficient (0-1 scale) -> percentage points ----113f = "arxiv-2510.03658v1.txt"114n_e5, e5 = find_line(f, r"^Treatment \| -0\.0911")115n_m5, m5 = find_line(f, r"^Mean Dep\. Var\. \|")116b5_s = cells(e5)[4]117b5 = float(b5_s)118v5 = 100 * b5119print(f"E5: beta={b5_s} -> {v5:.4f} pp (mean dep var {cells(m5)[4]})")120H["E5"] = dict(121    value=round(v5, 2), unit="percentage points",122    transformation=f"100*({b5_s}) = {v5:.2f} pp (exact rescaling from 0-1 probability scale)",123    harmonisable=True, missing_quantity=None,124    locator=f"arxiv-2510.03658v1 | Table 3 | row 'Treatment' | column '(4)' (extract line {n_e5})")125126data_issues = [127    {"issue": "arxiv-2609.27732v1: the abstract states fatal drug-related hospitalisations 'fall by 35%%', but the Table 7 5 km coefficient -0.354 log points implies 100*(exp(-0.354)-1) = %.1f%%; Section 6.1 says 'approximately 30%%'. The abstract figure appears to conflate the overall effect (-35.0%%) or log points with percent." % rel4,128     "handling": "Used the Table 7 coefficient as instructed; E4 is not harmonisable anyway (missing level). Flagged."},129    {"issue": "arxiv-2609.27732v1: the 5 km CS-DR ATT -0.431 has SE (0.210) in Table 6 but (0.218) in Table A19; Table 6/7 report 608 control regions while the A19 note says 640 never-treated comparison regions.",130     "handling": "Point estimates agree (-0.431); SEs not used (no uncertainty propagation). Flagged only."},131    {"issue": "arxiv-2609.27732v1: Section 6 text describes 'five diagnostic sub-categories' while the Figure 7 note mentions four; the Table 7 note lists four ICD sub-outcome groups and gives no definition for the 'Drug-related (fatal)' row.",132     "handling": "Did not affect the E1/E4 harmonisation; flagged."},133    {"issue": "arxiv-2505.04555v3: the Table 2 note describes the 2023 changes as 'about 4% minimum wage increase', while the table row '% MW changes' prints 0.047 (4.7%).",134     "handling": "Used the printed table value 0.047 as the target definition requires ('size of that increase as printed in the same table'); with 0.04 the result would be %.2f%%." % (100 * eta * 0.04)},135    {"issue": "arxiv-2510.03658v1: Section 4.1 refers to 'table 2' and a coefficient -0.0855 with dependent mean 0.455 (headline 18.79%%), which is not in the extract; the Table 3 column (4) '%% Change' prints -0.3685 whereas -0.0962/0.261 = %.4f (rounding difference in the last digit)." % (b5 / 0.261),136     "handling": "Used Table 3 column (4) as specified in effects.json; flagged."},137    {"issue": "arxiv-2606.14621v1: the abstract rounds the reemployment ATE to 1.5 pp versus 1.47 pp in Table 2; the control mean 75.0% is the raw (not block-adjusted) group mean.",138     "handling": "Used the Table 2 printed values 1.47 pp and 75.0% as the target definition specifies the control-group mean."},139]140141n_input_rows = {"effects.json": len(effects), "README.md": wc_l(os.path.join(DATA, "README.md"))}142for fn in sorted(os.listdir(SRC)):143    n_input_rows[fn] = wc_l(os.path.join(SRC, fn))144145n_h = sum(1 for v in H.values() if v["value"] is not None)146n_nh = sum(1 for v in H.values() if v["harmonisable"] is False)147res = dict(148    harmonised=H, n_harmonised=n_h, n_not_harmonisable=n_nh, task_id="F4-conf-03",149    feasibility="complete",150    feasibility_reason=None,151    data_issues=data_issues, n_input_rows=n_input_rows, n_analysed=len(effects), random_seed=None)152os.makedirs("out", exist_ok=True)153with open("out/results.json", "w", encoding="utf-8") as fh:154    json.dump(res, fh, indent=2, ensure_ascii=False)155print(json.dumps({k: (v["value"], v["harmonisable"]) for k, v in H.items()}))156
Files · main
Menu

Theme