use std::error::Error;
use ndarray::{Array1, Array2};
use linfa::prelude::*;
use linfa_linear::LinearRegression;
pub fn to_ndarrays(
descriptors: Vec<Vec<f64>>,
targets: Vec<f64>,
) -> Result<(Array2<f64>, Array1<f64>), Box<dyn Error>> {
let n_samples = descriptors.len();
if n_samples == 0 {
return Err("descriptors is empty".into());
}
let n_features = descriptors[0].len();
let mut flat: Vec<f64> = Vec::with_capacity(n_samples * n_features);
for row in &descriptors {
if row.len() != n_features {
return Err("inconsistent feature lengths in descriptors".into());
}
flat.extend_from_slice(&row[..]);
}
let x = Array2::from_shape_vec((n_samples, n_features), flat)
.map_err(|e| format!("failed to construct Array2: {}", e))?;
let y = Array1::from_vec(targets);
if y.len() != n_samples {
return Err("targets length does not match number of descriptor rows".into());
}
Ok((x, y))
}
pub fn train_and_predict_example() -> Result<Array1<f64>, Box<dyn Error>> {
let descriptors = vec![
vec![1.0_f64, 2.0_f64],
vec![2.0_f64, 3.0_f64],
vec![3.0_f64, 4.0_f64],
vec![4.0_f64, 5.0_f64],
];
let targets = vec![3.0_f64, 5.0_f64, 7.0_f64, 9.0_f64];
let (x, y) = to_ndarrays(descriptors, targets)?;
let dataset = Dataset::from((x.clone(), y.clone()));
let model = LinearRegression::default().fit(&dataset)?;
let new_sample = Array2::from_shape_vec((1, x.ncols()), vec![5.0_f64, 6.0_f64])?;
let prediction = model.predict(&new_sample);
Ok(prediction)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn to_ndarrays_and_train_example_runs() {
let pred = train_and_predict_example().expect("train_and_predict_example failed");
let value = pred[0];
assert!((value - 11.0).abs() < 1e-6, "unexpected prediction: {}", value);
}
#[test]
fn conversion_checks_shapes() {
let desc = vec![vec![1.0, 2.0], vec![3.0, 4.0]];
let tgt = vec![3.0, 7.0];
let (x, y) = to_ndarrays(desc, tgt).unwrap();
assert_eq!(x.shape(), &[2, 2]);
assert_eq!(y.len(), 2);
}
}