1#[derive(Debug, Copy, Clone, PartialEq)]
33pub struct LinearRegression {
34 pub slope: f64,
35 pub mean_x: f64,
36 pub mean_y: f64,
37}
38
39impl LinearRegression {
40 pub fn from_data<
41 'a,
42 S: 'a,
43 I: Iterator<Item = S> + Clone,
44 X: Fn(&S) -> f64,
45 Y: Fn(&S) -> f64,
46 >(
47 data: I,
48 x_accessor: X,
49 y_accessor: Y,
50 ) -> Option<LinearRegression> {
51 let mut mean_x = 0f64;
52 let mut mean_y = 0f64;
53 let mut count = 0f64;
54
55 for s in data.clone() {
56 let x = x_accessor(&s);
57 let y = y_accessor(&s);
58 mean_y += y;
59 mean_x += x;
60 count += 1.0;
61 }
62
63 if count <= 1. {
64 return None;
65 }
66 mean_x /= count;
67 mean_y /= count;
68
69 let mut xsum = 0f64;
70 let mut linear = 0f64;
71 for s in data {
72 let x = x_accessor(&s);
73 let y = y_accessor(&s);
74
75 let dy = y - mean_y;
76 let dx = x - mean_x;
77
78 xsum += dx * dx;
79 linear += dx * dy;
80 }
81 if xsum <= 0.0 {
82 return None;
83 }
84
85 Some(LinearRegression {
86 slope: linear / xsum,
87 mean_x,
88 mean_y,
89 })
90 }
91}
92
93#[cfg(test)]
94mod test {
95 use crate::fitting::LinearRegression;
96 use crate::sampling::Sample;
97 use irox_time::epoch::UnixTimestamp;
98
99 #[test]
100 pub fn test() {
101 let data = &[
102 Sample::new(0., UnixTimestamp::from_seconds(0)),
103 Sample::new(0.5, UnixTimestamp::from_seconds_f64(0.5)),
104 Sample::new(1., UnixTimestamp::from_seconds(1)),
105 ];
106
107 let reg = LinearRegression::from_data(
108 data.iter(),
109 |s| s.time.get_offset().as_seconds_f64(),
110 |s| s.value,
111 );
112 assert!(reg.is_some());
113 let Some(reg) = reg else {
114 return;
115 };
116 assert_eq!(1.0, reg.slope);
117 assert_eq!(0.5, reg.mean_x);
118 assert_eq!(0.5, reg.mean_y);
119 }
120}