"""Generate paper-consumable summaries from locked evaluation records.""" from __future__ import annotations from pathlib import Path from typing import Any import pandas as pd from .artifacts import write_json_immutable from .statistics import aggregate_draws_by_group, hierarchical_bootstrap_ci def summarize_results( results: pd.DataFrame, metrics: list[str], bootstrap_samples: int, confidence: float, seed: int, ) -> tuple[pd.DataFrame, pd.DataFrame]: aggregated = aggregate_draws_by_group(results, metrics) rows: list[dict[str, Any]] = [] for method, method_rows in aggregated.groupby("method"): for metric in metrics: lower, upper = hierarchical_bootstrap_ci( method_rows[metric], bootstrap_samples, confidence, seed ) rows.append( { "method": method, "metric": metric, "mean": float(method_rows[metric].mean()), "ci_lower": lower, "ci_upper": upper, "groups": int(method_rows["group_id"].nunique()), } ) return aggregated, pd.DataFrame(rows) def write_paper_outputs( output_dir: str | Path, results: pd.DataFrame, metrics: list[str], bootstrap_samples: int, confidence: float, seed: int, metadata: dict[str, Any], ) -> None: output = Path(output_dir) output.mkdir(parents=True, exist_ok=True) for name in ("query_results.csv", "group_results.csv", "summary.csv", "metadata.json"): if (output / name).exists(): raise FileExistsError(f"Refusing to overwrite paper output: {output / name}") aggregated, summary = summarize_results( results, metrics, bootstrap_samples, confidence, seed ) results.to_csv(output / "query_results.csv", index=False) aggregated.to_csv(output / "group_results.csv", index=False) summary.to_csv(output / "summary.csv", index=False) write_json_immutable(output / "metadata.json", metadata)