robust_rs/datasets.rs
1//! Classic robust-statistics datasets, embedded for tests and examples.
2//!
3//! Values are sourced from R's `robustbase`; see `datasets/data/*.csv`.
4
5use ndarray::{Array1, Array2};
6
7/// Brownlee's stack-loss data: 21 observations, predictors (air flow, water
8/// temperature, acid concentration) and the response (stack loss). Returns
9/// `(X, y)` with `X` shaped `21 × 3`.
10pub fn stackloss() -> (Array2<f64>, Array1<f64>) {
11 parse_csv(include_str!("datasets/data/stackloss.csv"), 3)
12}
13
14/// Hertzsprung–Russell diagram of star cluster CYG OB1: 47 stars, predictor
15/// `log.Te`, response `log.light`. Cases 11, 20, 30, 34 are giant-star
16/// outliers. Returns `(X, y)` with `X` shaped `47 × 1`.
17pub fn stars_cyg() -> (Array2<f64>, Array1<f64>) {
18 parse_csv(include_str!("datasets/data/starsCYG.csv"), 1)
19}
20
21/// Parse an embedded, header-carrying CSV into a design matrix of the first
22/// `n_features` columns and a response vector from the final column. Panics on
23/// malformed data, which for a compiled-in dataset is a build-time bug.
24fn parse_csv(text: &str, n_features: usize) -> (Array2<f64>, Array1<f64>) {
25 let rows: Vec<Vec<f64>> = text
26 .lines()
27 .skip(1)
28 .map(str::trim)
29 .filter(|l| !l.is_empty())
30 .map(|l| {
31 l.split(',')
32 .map(|f| {
33 f.trim()
34 .parse::<f64>()
35 .expect("dataset field is not numeric")
36 })
37 .collect()
38 })
39 .collect();
40
41 let n = rows.len();
42 let ncol = n_features + 1;
43 assert!(
44 rows.iter().all(|r| r.len() == ncol),
45 "dataset row width mismatch: expected {ncol} columns"
46 );
47
48 let mut x = Array2::zeros((n, n_features));
49 let mut y = Array1::zeros(n);
50 for (i, row) in rows.iter().enumerate() {
51 for j in 0..n_features {
52 x[[i, j]] = row[j];
53 }
54 y[i] = row[n_features];
55 }
56 (x, y)
57}