#!/usr/bin/env python3 # Author: Simon-Pierre Boucher — simon-pierre.boucher@uqo.ca """Phase 4 — NaN-safe Monte Carlo summary from results/simulation/metrics.parquet → summary.csv (means + Monte Carlo s.e.). Cells for which a method is not estimable (NaN) are excluded from that method's averages; `share_est` reports their share.""" from __future__ import annotations import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).resolve().parents[1] / "src")) import polars as pl from sharp_hpi import config as C OUT = C.RESULTS / "simulation" m = pl.read_parquet(OUT / "metrics.parquet") cols = ["rmse_ret", "mae_ret", "bias_ret", "rmse_lvl", "cov95", "cov95_ret", "corr_ret", "lag_xcorr", "revision_rmse", "share_est", "secs"] m = m.with_columns([pl.col(c).fill_nan(None) for c in cols]) # per replication first (so the Monte Carlo s.e. is over replications, not cells) rep = m.group_by(["scenario", "method", "level", "rep"]).agg([pl.col(c).mean().alias(c) for c in cols] + [pl.len().alias("cells")]) summ = rep.group_by(["scenario", "method", "level"]).agg( pl.col("cells").mean().alias("cells"), pl.col("rep").n_unique().alias("reps"), *[pl.col(c).mean().alias(c) for c in cols], *[(pl.col(c).std() / pl.col(c).count().sqrt()).alias(c + "_mcse") for c in ["rmse_ret", "bias_ret", "rmse_lvl", "cov95"]], ).sort(["scenario", "level", "rmse_ret"]) summ.write_csv(OUT / "summary.csv") pl.Config.set_tbl_rows(200); pl.Config.set_tbl_width_chars(220) print(summ.filter(pl.col("level") == "municipality").select(["scenario", "method", "reps", "rmse_ret", "rmse_ret_mcse", "bias_ret", "cov95", "lag_xcorr", "share_est"]))