55c676fb9b
Replace giant/analysis.py's dual numpy-SampleCollection + polars paths with a single polars-streaming implementation that produces the validation notebook's plots directly from a `giant predict --coord local` parquet, sized for files larger than RAM. - Drop the numpy SampleCollection path (load_predicted_local, marginal_table, correlation_matrices, direction_alignment, constraint_report, plot_kl_bars) and the rollout observables; the 5 remaining plotters now take a parquet path / LazyFrame and stream internally. - Rewrite compute_event_observables_pl to aggregate in parallel streaming polars (post-pos reconstruction as expressions) instead of a serial pyarrow-batch + numpy loop, fixing a pre-existing OOM (holistic median + 323M-row join in the bin-edge sizing). Medians are approximated from a streaming log-bin histogram with within-bin interpolation. - Keep every full-file scan narrow (few columns): on a file larger than RAM, peak mmap memory, not scan count, is the binding constraint. Marginals run one dim at a time (~15GB peak) rather than a combined all-dims pass (OOM). - Update analysis/validation.ipynb to the path-based API; delete the analysis/export_*.py and compare_ode_steps_*.py one-off scripts. - Rewrite tests/test_analysis.py around parquet fixtures with an inline numpy oracle; add correlation/streaming-plotter and approx-median coverage. Verified end-to-end on the 32GB predict file: full notebook completes at ~25GB peak (no OOM); event rollup runs at ~13 cores. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>