rusty_ai/metrics/
errors.rs1use std::error::Error;
2
3use nalgebra::DVector;
4
5use crate::data::dataset::RealNumber;
6
7pub trait RegressionMetrics<T: RealNumber> {
9 fn mse(&self, y_true: &DVector<T>, y_pred: &DVector<T>) -> Result<T, Box<dyn Error>> {
24 if y_true.len() != y_pred.len() {
25 return Err("Predictions and labels are of different sizes.".into());
26 }
27
28 let n = T::from_usize(y_true.len()).unwrap();
29 let errors = y_pred - y_true;
30 let errors_sq = errors.component_mul(&errors);
31
32 Ok(errors_sq.sum() / n)
33 }
34
35 fn mae(&self, y_true: &DVector<T>, y_pred: &DVector<T>) -> Result<T, Box<dyn Error>> {
50 if y_true.len() != y_pred.len() {
51 return Err("Predictions and labels are of different sizes.".into());
52 }
53 let n = T::from_usize(y_true.len()).unwrap();
54 let abs_errors_sum = y_pred
55 .iter()
56 .zip(y_true.iter())
57 .map(|(&y_p, &y_t)| (y_p - y_t).abs())
58 .fold(T::from_f64(0.0).unwrap(), |acc, x| acc + x);
59
60 Ok(abs_errors_sum / n)
61 }
62
63 fn r2(&self, y_true: &DVector<T>, y_pred: &DVector<T>) -> Result<T, Box<dyn Error>> {
78 if y_true.len() != y_pred.len() {
79 return Err("Predictions and labels are of different sizes.".into());
80 }
81 let n = T::from_usize(y_true.len()).unwrap();
82
83 let y_true_mean = y_true.sum() / n;
84
85 let y_true_mean_vec = DVector::from_element(y_true.len(), y_true_mean);
86
87 let mse_model = self.mse(y_true, y_pred)?;
88 let mse_base = self.mse(&y_true_mean_vec, y_true)?;
89
90 Ok(T::from_f64(1.0).unwrap() - (mse_model / mse_base))
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use nalgebra::DVector;
98
99 struct MockRegressor;
100
101 impl RegressionMetrics<f64> for MockRegressor {}
102
103 #[test]
104 fn test_mse() {
105 let regressor = MockRegressor;
106 let y_true = DVector::from_vec(vec![1.0, 2.0, 3.0]);
107 let y_pred = DVector::from_vec(vec![1.1, 1.9, 3.2]);
108
109 let mse = regressor.mse(&y_true, &y_pred).unwrap();
110 let expected_mse = ((0.1 * 0.1) + (0.1 * 0.1) + (0.2 * 0.2)) / 3.0;
111 assert!((mse - expected_mse).abs() < 1e-6);
112 }
113
114 #[test]
115 fn test_mae() {
116 let regressor = MockRegressor;
117 let y_true = DVector::from_vec(vec![1.0, 2.0, 3.0]);
118 let y_pred = DVector::from_vec(vec![1.1, 1.9, 3.2]);
119
120 let mae = regressor.mae(&y_true, &y_pred).unwrap();
121 let expected_mae = (0.1 + 0.1 + 0.2) / 3.0;
122 assert!((mae - expected_mae).abs() < 1e-6);
123 }
124
125 #[test]
126 fn test_r2() {
127 let regressor = MockRegressor;
128 let y_true = DVector::from_vec(vec![1.0, 2.0, 3.0]);
129 let y_pred = DVector::from_vec(vec![1.1, 1.9, 3.2]);
130
131 let r2 = regressor.r2(&y_true, &y_pred).unwrap();
132
133 let y_true_mean = y_true.mean();
134 let tss: f64 = y_true.iter().map(|&y| (y - y_true_mean).powi(2)).sum();
135
136 let rss: f64 = y_true
137 .iter()
138 .zip(y_pred.iter())
139 .map(|(&y_t, &y_p)| (y_t - y_p).powi(2))
140 .sum();
141
142 let expected_r2 = 1.0 - (rss / tss);
144
145 assert!((r2 - expected_r2).abs() < 1e-6);
146 }
147
148 #[test]
149 fn test_different_length_error() {
150 let regressor = MockRegressor;
151 let y_true = DVector::from_vec(vec![1.0, 2.0, 3.0]);
152 let y_pred = DVector::from_vec(vec![1.1, 1.9]);
153
154 assert!(regressor.mse(&y_true, &y_pred).is_err());
155 assert!(regressor.mae(&y_true, &y_pred).is_err());
156 assert!(regressor.r2(&y_true, &y_pred).is_err());
157 }
158}