`_row_subsample` hash-filters on `pre_E`, but `build_context`'s
prediction branch called it on a frame already projected down to
`pred_<var>`/`true_<var>` columns, so any run whose prediction file
exceeds `sample_rows` (the default is 1M; real predict outputs can be
100M+ rows) failed with `ColumnNotFoundError: pre_E`.
Subsample the full paired frame first, then project — matching every
other _row_subsample call site — and restructure the loop to subsample
once per prediction side instead of once per paired variable, cutting
6 streaming passes over the prediction file down to 1.
Add a regression test with sample_rows below the fixture's row count
so the hash-filter branch is actually exercised (the existing test
never took it).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Qb7xBAa6aAR94AzimgpPxq
Adds a `prediction` plot family to `giant analyze`, alongside the existing
rollout-vs-reference comparison, and extends `giant predict` to make it
possible:
- `giant predict --coord global` gains schema v3 (`--truth/--no-truth`,
default on): writes true_* physical columns and true secondary lists
alongside the predictions, so the output is fully paired.
- New `giant/analysis/prediction.py` builds one canonical true/pred frame
(`paired_frame`) from either predict coord mode.
- `catalog.py` gains 35 `pred_*` specs: marginals, 2D truth-vs-pred scatter
(new `heatmap2d` kind), residuals/relative-residuals/calibration profiles,
KS/bias/RMSE scorecards, n_sec + secondary-species confusion matrices,
direction-alignment and constraint-violation checks, and a correlation
delta. Two new Reduced kinds (`paired_hist`, `heatmap2d`) get renderers.
Every spec degrades to kind="unavailable" with no --prediction given.
- `condor.py`/`cli.py`: `--prediction`/`--prediction-label` on
`analyze prep`/`submit`, threaded through RunMeta and every compute job.
Full test suite (1162 tests), ruff, and ty all pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PpxE9nij3ujg9XcuzvQ26q