File size: 2,762 Bytes
3ecb175
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
"""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()