robust_rs/location/
m_location.rs1use robust_rs_core::error::RobustError;
4use robust_rs_core::rho::RhoFunction;
5use robust_rs_core::scale::ScaleEstimator;
6use robust_rs_core::solver::Control;
7use robust_rs_core::types::Scale;
8
9#[derive(Debug, Clone, Copy)]
11pub struct LocationFit {
12 pub estimate: f64,
14 pub scale: Scale,
16 pub iters: usize,
18}
19
20pub fn m_location(
23 data: &[f64],
24 rho: &dyn RhoFunction,
25 scale: &dyn ScaleEstimator,
26 ctrl: &Control,
27) -> Result<LocationFit, RobustError> {
28 if data.is_empty() {
29 return Err(RobustError::InsufficientData { needed: 1, got: 0 });
30 }
31
32 let scale_est = scale.scale(data)?;
35 let s: f64 = scale_est.get();
36
37 let mut buf = data.to_vec();
38 let mut theta = median(&mut buf); for i in 1..=ctrl.max_iter {
41 let (mut sw, mut swx) = (0.0_f64, 0.0_f64);
43 for &x in data {
44 let w = rho.weight((x - theta) / s);
45 sw += w;
46 swx += w * x;
47 }
48 let next = swx / sw;
49 if !next.is_finite() {
50 return Err(RobustError::SingularDesign);
52 }
53 if (next - theta).abs() <= ctrl.tol * s {
56 return Ok(LocationFit {
57 estimate: next,
58 scale: scale_est,
59 iters: i,
60 });
61 }
62 theta = next;
63 }
64 Err(RobustError::NonConvergence {
65 iters: ctrl.max_iter,
66 })
67}
68
69fn median(v: &mut [f64]) -> f64 {
71 v.sort_unstable_by(f64::total_cmp);
72 let n = v.len();
73 let mid = n / 2;
74 if n % 2 == 1 {
75 v[mid]
76 } else {
77 0.5 * (v[mid - 1] + v[mid])
78 }
79}