TuberVision / scripts /run_loto_models.py
cisdeli
feat: ports codebase from notebooks
3ecb175
Raw History Blame Contribute Delete
2.76 kB
"""Run M1, M2, M3, and RF under leave-one-trial-out yield prediction.
Adds the M3 structural features, runs LOTO for each of the four models,
and writes the per-fold metrics, per-plot predictions, and the cross-fold
summary.
Usage:
uv run python scripts/run_loto_models.py \\
--input outputs/df_model_yield_by_plot.csv \\
--out-dir outputs/loto/
"""
from __future__ import annotations
import argparse
from pathlib import Path
import pandas as pd
from potatopheno.evaluate import (
run_loto_linear,
run_loto_rf,
summarize_across_trials,
)
from potatopheno.models import (
PREDICTORS_M1,
PREDICTORS_M2,
PREDICTORS_M3,
PREDICTORS_RF,
RESPONSE,
RF_KWARGS,
add_model_features,
)
def _parse_args() -> argparse.Namespace:
p = argparse.ArgumentParser(description=__doc__.splitlines()[0])
p.add_argument(
"--input",
type=Path,
required=True,
help="Plot-level model dataset (output of build_plot_model_dataset.py).",
)
p.add_argument(
"--out-dir",
type=Path,
required=True,
help="Directory to write the metrics, predictions, and summary CSVs.",
)
return p.parse_args()
def main() -> None:
args = _parse_args()
args.out_dir.mkdir(parents=True, exist_ok=True)
df = pd.read_csv(args.input)
df = add_model_features(df)
runs = [
("M1", run_loto_linear, PREDICTORS_M1, {}),
("M2", run_loto_linear, PREDICTORS_M2, {}),
("M3", run_loto_linear, PREDICTORS_M3, {}),
("RF", run_loto_rf, PREDICTORS_RF, RF_KWARGS),
]
pred_parts: list[pd.DataFrame] = []
metric_parts: list[pd.DataFrame] = []
for name, runner, predictors, extra in runs:
df_pred, df_metrics = runner(df, predictors, RESPONSE, name, **extra)
pred_parts.append(df_pred)
metric_parts.append(df_metrics)
df_pred_all = pd.concat(pred_parts, ignore_index=True)
df_metrics_all = (
pd.concat(metric_parts, ignore_index=True)
.sort_values(["model", "test_trial"])
.reset_index(drop=True)
)
df_summary = summarize_across_trials(df_metrics_all).sort_values("mean_MAPE")
df_metrics_all.to_csv(args.out_dir / "loto_metrics_by_fold.csv", index=False)
df_pred_all.to_csv(args.out_dir / "loto_predictions_by_plot.csv", index=False)
df_summary.to_csv(args.out_dir / "loto_summary_by_model.csv", index=False)
print("\nLOTO metrics by fold:\n")
print(df_metrics_all.round(3).to_string(index=False))
print("\nLOTO summary across folds (mean of trial-level metrics):\n")
print(df_summary.round(3).to_string(index=False))
print(f"\n[DONE] wrote 3 CSVs → {args.out_dir}")
if __name__ == "__main__":
main()