SPB Git

spb/wp29_uqo

Public
4.0 KB · 94 lines python
1"""F2-conf-03: Monte Carlo coverage of OLS CIs under fixed heteroskedastic design.2Run from workspace root: ~/wp29/venv/bin/python out/analysis.py3Reads only data/design.csv; writes out/results.json and out/exclusions.csv. Seed fixed."""4import json, hashlib, os5import numpy as np, pandas as pd6from scipy import stats78SEED = 202610309ALPHA, BETA, N, R, LEVEL = 1.0, 0.5, 30, 2000, 0.9510os.makedirs("out", exist_ok=True)1112path = "data/design.csv"13raw = pd.read_csv(path)14n_raw = len(raw)15issues = []16# audit checks (no filtering expected)17d = raw.copy()18excl = []19n0 = len(d)20d = d.dropna(subset=["x", "sigma"])21excl.append(("1", "drop rows with missing x or sigma", n0, len(d), n0 - len(d), "model requires x and sigma"))22n0 = len(d)23d = d[d["sigma"] > 0]24excl.append(("2", "drop rows with sigma <= 0", n0, len(d), n0 - len(d), "sigma is a standard deviation"))25n0 = len(d)26d = d.drop_duplicates(subset=["i"])27excl.append(("3", "drop duplicate observation index i", n0, len(d), n0 - len(d), "each i appears once in design"))28pd.DataFrame(excl, columns=["step", "rule", "n_before", "n_after", "n_removed", "rationale"]).to_csv("out/exclusions.csv", index=False)29for s in excl:30    if s[4] > 0:31        issues.append({"issue": f"{s[4]} rows removed by rule '{s[1]}'", "handling": "excluded"})32if len(d) != N:33    issues.append({"issue": f"design has {len(d)} usable rows, expected {N}", "handling": "reported; feasibility partial"})3435d = d.sort_values("i")36x = d["x"].to_numpy(float); sig = d["sigma"].to_numpy(float); n = len(x)37X = np.column_stack([np.ones(n), x])38XtX_inv = np.linalg.inv(X.T @ X)39H = XtX_inv @ X.T                      # 2 x n, beta_hat = H y40w = H[1]                               # slope weights41sd_exact = float(np.sqrt(np.sum(w**2 * sig**2)))  # exact: Var(b) = sum w_i^2 sigma_i^24243rng = np.random.default_rng(SEED)44eps = rng.standard_normal((R, n))45Y = ALPHA + BETA * x[None, :] + sig[None, :] * eps   # R x n46B = Y @ H.T                                           # R x 247resid = Y - B @ X.T48s2 = np.sum(resid**2, axis=1) / (n - 2)49se_conv = np.sqrt(s2 * XtX_inv[1, 1])50hc0 = np.sum((w[None, :] ** 2) * resid**2, axis=1)    # sandwich [ (X'X)^-1 X' diag(e^2) X (X'X)^-1 ]_{11}51se_hc1 = np.sqrt(hc0 * n / (n - 2))52tcrit = stats.t.ppf(0.5 + LEVEL / 2, n - 2)53b = B[:, 1]54cov_conv = float(np.mean(np.abs(b - BETA) <= tcrit * se_conv))55cov_hc1 = float(np.mean(np.abs(b - BETA) <= tcrit * se_hc1))56mcse_nominal = float(np.sqrt(LEVEL * (1 - LEVEL) / R))57mcse_conv = float(np.sqrt(cov_conv * (1 - cov_conv) / R))58mcse_hc1 = float(np.sqrt(cov_hc1 * (1 - cov_hc1) / R))59undercovers = bool(cov_conv < LEVEL - 3 * mcse_nominal)60# exact expectation of conventional variance (for checking mean_se_conventional): E[s2]=tr(M Sigma)/(n-2)61M = np.eye(n) - X @ H62E_s2 = float(np.trace(M @ np.diag(sig**2)) / (n - 2))6364res = {65    "task_id": "F2-conf-03",66    "coverage_conventional": cov_conv,67    "coverage_hc1": cov_hc1,68    "sd_beta_exact": sd_exact,69    "sd_beta_mc": float(np.std(b, ddof=1)),70    "mean_se_conventional": float(np.mean(se_conv)),71    "conventional_ci_undercovers": undercovers,72    "n_replications": R,73    "n_obs_per_replication": int(n),74    "feasibility": "complete" if n == N else "partial",75    "feasibility_reason": None if n == N else "design row count differs from 30",76    "data_issues": issues,77    "n_input_rows": {"design.csv": int(n_raw)},78    "random_seed": SEED,79    # auxiliary80    "t_critical_28": float(tcrit),81    "mcse_coverage_conventional": mcse_conv,82    "mcse_coverage_hc1": mcse_hc1,83    "mcse_at_nominal_0.95": mcse_nominal,84    "undercover_threshold": float(LEVEL - 3 * mcse_nominal),85    "undercovers_using_estimated_p_se": bool(cov_conv < LEVEL - 3 * mcse_conv),86    "mean_se_hc1": float(np.mean(se_hc1)),87    "se_conv_population_sqrtEs2": float(np.sqrt(E_s2 * XtX_inv[1, 1])),88    "mean_beta_mc": float(np.mean(b)),89    "design_sha256": hashlib.sha256(open(path, "rb").read()).hexdigest(),90}91with open("out/results.json", "w") as f:92    json.dump(res, f, indent=2)93print(json.dumps(res, indent=2))94
Files · main
Menu

Theme