robust_rs_core/scale/
mad.rs1use crate::error::RobustError;
4use crate::scale::ScaleEstimator;
5use crate::types::Scale;
6
7#[derive(Debug, Clone, Copy)]
10pub struct Mad {
11 pub consistency: f64,
13}
14
15impl Default for Mad {
16 fn default() -> Self {
17 Self {
18 consistency: 1.482_602_218_505_602,
19 }
20 }
21}
22
23impl ScaleEstimator for Mad {
24 fn scale(&self, residuals: &[f64]) -> Result<Scale, RobustError> {
25 if residuals.is_empty() {
26 return Err(RobustError::InsufficientData { needed: 1, got: 0 });
27 }
28
29 let mut buf = residuals.to_vec();
31
32 let center = median(&mut buf);
33 for x in buf.iter_mut() {
34 *x = (*x - center).abs(); }
36 let mad = median(&mut buf);
37
38 let s = mad * self.consistency;
39 if !s.is_finite() || s <= 0.0 {
43 return Err(RobustError::DegenerateScale);
44 }
45 Scale::new(s)
46 }
47}
48
49fn median(v: &mut [f64]) -> f64 {
53 v.sort_unstable_by(f64::total_cmp);
54 let n = v.len();
55 let mid = n / 2;
56 if n % 2 == 1 {
57 v[mid]
58 } else {
59 0.5 * (v[mid - 1] + v[mid])
60 }
61}