SPB Git

spb/wp29_uqo

Public
8.7 KB · 154 lines python
1"""F4-conf-03: harmonise five printed effect sizes onto target definitions.2Reads only data/; writes out/results.json deterministically. No randomness."""3import json, math, os, re45ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))6D = os.path.join(ROOT, "data")7SRC = os.path.join(D, "sources")8910def lines_of(path):11    with open(path, encoding="utf-8") as fh:12        return fh.read().split("\n")131415def wc_l(path):16    with open(path, "rb") as fh:17        return fh.read().count(b"\n")181920def find_row(src, label, after=None):21    """Return (line_no, cells) of the first table row whose first cell == label22    (searching after a line containing `after`, if given)."""23    L = lines_of(os.path.join(SRC, src + ".txt"))24    start = 025    if after:26        start = next(i for i, l in enumerate(L) if after in l)27    for i in range(start, len(L)):28        cells = [c.strip() for c in L[i].split("|")]29        if cells[0] == label and len(cells) > 1:30            print(f"[{src} line {i+1}] {L[i]}")31            return i + 1, cells32    raise ValueError(f"row {label!r} not found in {src}")333435def num(cell):36    m = re.search(r"-?\d[\d,]*\.?\d*", cell.replace("−", "-"))37    return float(m.group(0).replace(",", ""))383940effects = json.load(open(os.path.join(D, "effects.json"), encoding="utf-8"))41ids = [e["id"] for e in effects]42assert ids == ["E1", "E2", "E3", "E4", "E5"], ids4344H, baseline = {}, {}4546# ---- E1: DCR Table 6, ATT, 5 km (log points) -> % change47s = "arxiv-2609.27732v1"48ln_hdr, hdr = find_row(s, "", after="Table 6: Overall Average")49col5 = hdr.index("5 km")50ln, row = find_row(s, "ATT", after="Table 6: Overall Average")51b1 = num(row[col5])52ln_pc, pc = find_row(s, "% change", after="Table 6: Overall Average")53v1 = 100 * (math.exp(b1) - 1)54baseline["E1"] = {"printed_value": b1, "printed_unit": "log points"}55H["E1"] = {"value": round(v1, 4), "unit": "percent of the counterfactual hospitalisation rate",56           "transformation": f"100*(exp(b)-1) with b={row[col5]} -> 100*(exp({b1})-1) = {v1:.4f}% (cross-check: printed '% change' row = {pc[col5]})",57           "harmonisable": True, "missing_quantity": None,58           "locator": f"{s} | Table 6 | row 'ATT' (line {ln}) | column '5 km'; cross-check row '% change' (line {ln_pc}) | column '5 km'"}5960# ---- E2: RESEA Table 2, Reemployment, ATE (pp) / control mean (%)61s = "arxiv-2606.14621v1"62ln_h, hdr = find_row(s, "Outcome", after="[Table 2")63ln, row = find_row(s, "Reemployment", after="[Table 2")64ate = num(row[hdr.index("Average treatment effect estimate")])65ctrl = num(row[hdr.index("Control")])66v2 = 100 * ate / ctrl67baseline["E2"] = {"printed_value": ate, "printed_unit": "percentage points"}68H["E2"] = {"value": round(v2, 4), "unit": "percent of the control-group mean reemployment rate",69           "transformation": f"100*Delta/p with Delta={ate} pp, p={ctrl}% -> 100*{ate}/{ctrl} = {v2:.4f}%",70           "harmonisable": True, "missing_quantity": None,71           "locator": f"{s} | Table 2 | row 'Reemployment' (line {ln}) | column 'Average treatment effect estimate' (1.47 pp) and column 'Control' (75.0%)"}7273# ---- E3: MW Table 2, elasticity x printed % MW change74s = "arxiv-2505.04555v3"75ln_e, row_e = find_row(s, "Employment elasticity w.r.t MW", after="Table 2:")76ln_g, row_g = find_row(s, "% MW changes", after="Table 2:")77eta, g = num(row_e[1]), num(row_g[1])78v3 = 100 * eta * g79baseline["E3"] = {"printed_value": eta, "printed_unit": "elasticity (dimensionless)"}80H["E3"] = {"value": round(v3, 4), "unit": "percent change in employment",81           "transformation": f"100*eta*g with eta={row_e[1]}, g={row_g[1]} (proportional MW change) -> 100*({eta})*({g}) = {v3:.4f}%",82           "harmonisable": True, "missing_quantity": None,83           "locator": f"{s} | Table 2 | row 'Employment elasticity w.r.t MW' (line {ln_e}) | column 'Estimate (SE)'; row '% MW changes' (line {ln_g}) | column 'Estimate (SE)'"}8485# ---- E4: DCR Table 7, fatal, 5 km -> absolute change per 100,000: needs level86s = "arxiv-2609.27732v1"87ln_h7, hdr7 = find_row(s, "Outcome", after="Table 7:")88ln, row = find_row(s, "Drug-related (fatal)", after="Table 7:")89b4 = num(row[hdr7.index("5 km")])90rel4 = 100 * (math.exp(b4) - 1)91baseline["E4"] = {"printed_value": b4, "printed_unit": "log points",92                  "relative_percent_change_reachable": round(rel4, 4)}93H["E4"] = {"value": None, "unit": "hospitalisations per 100,000 inhabitants per year",94           "transformation": f"would be R0*(exp(b)-1) with b={row[hdr7.index('5 km')]}; the relative part 100*(exp({b4})-1) = {rel4:.4f}% is computable, but the level R0 is not printed",95           "harmonisable": False,96           "missing_quantity": "baseline/counterfactual level (mean) of the fatal drug-related hospitalisation rate per 100,000 inhabitants per year for the treated regions within 5 km (e.g. pre-opening or untreated-potential-outcome mean); not printed in the extract",97           "locator": f"{s} | Table 7 | row 'Drug-related (fatal)' (line {ln}) | column '5 km'"}9899# ---- E5: Tossou Table 3, Treatment, col (4): 0-1 scale -> pp100s = "arxiv-2510.03658v1"101ln_h, hdr = find_row(s, "", after="Table 3: Probability")102ln_h2, hdr2 = find_row(s, "", after="| (1) | (2)")103ln, row = find_row(s, "Treatment", after="Table 3: Probability")104c4 = hdr2.index("(4)")105beta = num(row[c4])106v5 = 100 * beta107ln_m, rowm = find_row(s, "Mean Dep. Var.", after="Table 3: Probability")108baseline["E5"] = {"printed_value": beta, "printed_unit": "change in probability (0-1 scale)"}109H["E5"] = {"value": round(v5, 4), "unit": "percentage points",110           "transformation": f"100*beta with beta={row[c4]} -> {v5:.4f} pp (exact unit rescaling)",111           "harmonisable": True, "missing_quantity": None,112           "locator": f"{s} | Table 3 | row 'Treatment' (line {ln}) | column '(4)'"}113114data_issues = [115    {"issue": "arxiv-2609.27732v1: abstract states fatal drug-related hospitalisations 'fall by 35%', but Table 7 (5 km) prints -0.354 log points, i.e. 100*(exp(-0.354)-1) = -29.8%, and Section 6.1 says 'approximately 30%'.",116     "handling": "Table 7 value used for E4; abstract figure not used."},117    {"issue": "arxiv-2609.27732v1: Table A19 prints SE (0.218) for the CS-DR ATT -0.431 while Table 6 prints (0.210); A19 note mentions 640 never-treated regions while the tables print 608 control regions.",118     "handling": "Point estimate identical in both tables; Table 6 used for E1. SEs not propagated."},119    {"issue": "arxiv-2609.27732v1: Table A19 '% change' entries for Synthetic DiD (-30.3%) and Stacked DiD (-13.9%) differ by about 0.1 from 100*(exp(b)-1) (-30.37%, -13.84%).",120     "handling": "Not used; noted only. The Table 6 '% change' for 5 km (-35.0%) agrees with the transform of -0.431 (-35.01%)."},121    {"issue": "arxiv-2505.04555v3: Table 2 prints '% MW changes | 0.047' while the table note says 'about 4% minimum wage increase'; the row label '%' with value 0.047 is ambiguous between a proportion (4.7%) and a percent (0.047%).",122     "handling": "Table value used as instructed, read as a proportion g = 0.047 (4.7%), consistent in magnitude with the note's 'about 4%'. With g = 0.04 the result would be -1.55%."},123    {"issue": "arxiv-2510.03658v1: Table 3 col (4) prints '% Change -0.3685' while -0.0962/0.261 = -0.3686 (printed mean rounded); text refers to a Table 2 (-0.0855/0.455) that is not in the extract; footnote markers are rendered as '^9^9 9' etc.",124     "handling": "Not needed for the pp conversion of E5; noted only. Rendering artefacts ignored."},125    {"issue": "arxiv-2606.14621v1: the control mean 75.0% is the unadjusted aggregate control-group mean (rounded to 0.1), whereas the 1.47 pp ATE is adjusted for randomisation week; the printed control mean is therefore only an approximation of the counterfactual level for the ATE.",126     "handling": "Used as the denominator because the target definition specifies the control-group mean."},127]128129n_input_rows = {"effects.json": len(effects), "README.md": wc_l(os.path.join(D, "README.md"))}130for f in sorted(os.listdir(SRC)):131    n_input_rows[f] = wc_l(os.path.join(SRC, f))132133n_h = sum(1 for v in H.values() if v["value"] is not None)134n_nh = sum(1 for v in H.values() if v["harmonisable"] is False)135results = {136    "task_id": "F4-conf-03",137    "harmonised": H,138    "n_harmonised": n_h,139    "n_not_harmonisable": n_nh,140    "feasibility": "complete",141    "feasibility_reason": None,142    "data_issues": data_issues,143    "n_input_rows": n_input_rows,144    "n_analysed": len(effects),145    "random_seed": None,146    "baseline_printed": baseline,147}148os.makedirs(os.path.join(ROOT, "out"), exist_ok=True)149with open(os.path.join(ROOT, "out", "results.json"), "w", encoding="utf-8") as fh:150    json.dump(results, fh, indent=2, ensure_ascii=False)151for k, v in H.items():152    print(k, v["value"], v["unit"], "| harmonisable:", v["harmonisable"])153print("n_harmonised", n_h, "n_not_harmonisable", n_nh)154
Files · main
Menu

Theme