tease 0.4.0

Rapidly prototype interfaces for your machine learning models
Documentation
use smartcore::{
    dataset::iris, linalg::basic::matrix::DenseMatrix, naive_bayes::gaussian::GaussianNB,
};
use tease::{Input, Output, Teaser, Value};

fn main() -> Result<(), tease::Error> {
    demo().run()
}

fn demo() -> Teaser {
    let labels = [
        "Sepal length (cm)",
        "Sepal width (cm)",
        "Petal length (cm)",
        "Petal width (cm)",
    ];
    let sample = [5.1, 3.5, 1.4, 0.2];
    Teaser::default()
        .with_title("SmartCore iris classification")
        .with_description("A Gaussian naive Bayes model fitted to the Iris dataset. Measurements are in centimeters.")
        .with_inputs(labels.into_iter().zip(sample).map(|(label, value)| Input::Number {
            label: Some(label.into()), initial_value: value, min: Some(0.1), max: Some(10.0),
        }).collect())
        .with_output(Output::text("Predicted species"))
        .with_predictor_factory(|| {
            let dataset = iris::load_dataset();
            let rows: Vec<Vec<f64>> = dataset.data.chunks(dataset.num_features)
                .map(|row| row.iter().map(|&value| f64::from(value)).collect()).collect();
            let x = DenseMatrix::from_2d_vec(&rows).map_err(|error| error.to_string())?;
            let y: Vec<u32> = dataset.target;
            let model = GaussianNB::fit(&x, &y, Default::default()).map_err(|error| error.to_string())?;
            Ok(move |values: &[Value]| {
                let row = values.iter().map(Value::as_number).collect::<Result<Vec<_>, _>>()?;
                let x = DenseMatrix::from_2d_vec(&vec![row]).map_err(|error| error.to_string())?;
                let prediction = model.predict(&x).map_err(|error| error.to_string())?;
                let label = match prediction[0] {
                    0 => "Iris setosa", 1 => "Iris versicolor", 2 => "Iris virginica",
                    _ => return Err("The model returned an unknown species.".into()),
                };
                Ok(vec![Value::Text(label.into())])
            })
        })
}

#[test]
fn documented_samples_produce_all_three_species() {
    let session = demo().prepare().unwrap();
    for (sample, species) in [
        ([5.1, 3.5, 1.4, 0.2], "Iris setosa"),
        ([6.0, 2.9, 4.5, 1.5], "Iris versicolor"),
        ([6.9, 3.1, 5.4, 2.1], "Iris virginica"),
    ] {
        assert_eq!(
            session.predict(&sample.map(Value::Number)).unwrap(),
            [Value::Text(species.into())]
        );
    }
}