model-selection-rs 0.1.0

Cross-validation and model-selection utilities for Rust: stratified / group-aware / time-series splitting, nested CV, and learning & validation curves. Dependency-light, composes with any modeling crate.
Documentation
//! Rolling-origin time-series splitting: expanding window, fixed window, and gap.
//!
//! Run with: `cargo run --example time_series_split`

use model_selection_rs::splitters::{CvSplitter, TimeSeriesSplit};

fn show(name: &str, tss: &TimeSeriesSplit, n: usize) {
    println!("== {name} ==");
    for (f, (train, test)) in tss.split(n).unwrap().iter().enumerate() {
        println!(
            "  fold {f}: train [{}..={}] ({} samples)  ->  test [{}..={}]",
            train.first().unwrap(),
            train.last().unwrap(),
            train.len(),
            test.first().unwrap(),
            test.last().unwrap(),
        );
    }
    println!();
}

fn main() {
    let n = 20;

    // Expanding window: training set grows every split.
    show(
        "expanding window (n_splits=4)",
        &TimeSeriesSplit::new(4).unwrap(),
        n,
    );

    // Fixed rolling window: training set capped at 5 samples.
    show(
        "fixed window (max_train_size=5)",
        &TimeSeriesSplit::new(4).unwrap().with_max_train_size(5),
        n,
    );

    // Gap: two samples dropped between train and test (e.g. label delay).
    show(
        "gapped (gap=2)",
        &TimeSeriesSplit::new(3).unwrap().with_gap(2),
        n,
    );

    println!("Note: samples are assumed to be in chronological row order; every");
    println!("training index is strictly earlier than every test index in its fold.");
}