oxihuman_core/
running_statistics.rs1#![allow(dead_code)]
4
5pub struct RunningStatistics {
9 count: u64,
10 mean: f64,
11 m2: f64,
12 min: f64,
13 max: f64,
14}
15
16pub fn new_running_stats() -> RunningStatistics {
18 RunningStatistics {
19 count: 0,
20 mean: 0.0,
21 m2: 0.0,
22 min: f64::INFINITY,
23 max: f64::NEG_INFINITY,
24 }
25}
26
27impl RunningStatistics {
28 pub fn add(&mut self, x: f64) {
30 self.count += 1;
31 let delta = x - self.mean;
32 self.mean += delta / self.count as f64;
33 let delta2 = x - self.mean;
34 self.m2 += delta * delta2;
35 if x < self.min {
36 self.min = x;
37 }
38 if x > self.max {
39 self.max = x;
40 }
41 }
42
43 pub fn add_slice(&mut self, xs: &[f64]) {
45 for &x in xs {
46 self.add(x);
47 }
48 }
49
50 pub fn count(&self) -> u64 {
52 self.count
53 }
54
55 pub fn mean(&self) -> f64 {
57 self.mean
58 }
59
60 pub fn variance(&self) -> f64 {
62 if self.count < 2 {
63 return 0.0;
64 }
65 self.m2 / (self.count - 1) as f64
66 }
67
68 pub fn pop_variance(&self) -> f64 {
70 if self.count == 0 {
71 return 0.0;
72 }
73 self.m2 / self.count as f64
74 }
75
76 pub fn std_dev(&self) -> f64 {
78 self.variance().sqrt()
79 }
80
81 pub fn min(&self) -> Option<f64> {
83 if self.count == 0 {
84 None
85 } else {
86 Some(self.min)
87 }
88 }
89
90 pub fn max(&self) -> Option<f64> {
92 if self.count == 0 {
93 None
94 } else {
95 Some(self.max)
96 }
97 }
98
99 pub fn reset(&mut self) {
101 self.count = 0;
102 self.mean = 0.0;
103 self.m2 = 0.0;
104 self.min = f64::INFINITY;
105 self.max = f64::NEG_INFINITY;
106 }
107}
108
109pub fn slice_mean(xs: &[f64]) -> f64 {
111 if xs.is_empty() {
112 return 0.0;
113 }
114 xs.iter().sum::<f64>() / xs.len() as f64
115}
116
117pub fn slice_variance(xs: &[f64]) -> f64 {
119 let mut rs = new_running_stats();
120 rs.add_slice(xs);
121 rs.variance()
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127
128 #[test]
129 fn test_empty_count() {
130 let rs = new_running_stats();
132 assert_eq!(rs.count(), 0);
133 }
134
135 #[test]
136 fn test_mean_single() {
137 let mut rs = new_running_stats();
139 rs.add(7.0);
140 assert!((rs.mean() - 7.0).abs() < 1e-12);
141 }
142
143 #[test]
144 fn test_mean_multiple() {
145 let mut rs = new_running_stats();
147 rs.add_slice(&[1.0, 2.0, 3.0, 4.0, 5.0]);
148 assert!((rs.mean() - 3.0).abs() < 1e-12);
149 }
150
151 #[test]
152 fn test_variance() {
153 let mut rs = new_running_stats();
155 rs.add_slice(&[2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0]);
156 assert!(
157 (rs.variance() - 4.571428).abs() < 0.01,
158 "var={}",
159 rs.variance()
160 );
161 }
162
163 #[test]
164 fn test_std_dev_constant() {
165 let mut rs = new_running_stats();
167 rs.add_slice(&[5.0, 5.0, 5.0, 5.0]);
168 assert!(rs.std_dev() < 1e-12);
169 }
170
171 #[test]
172 fn test_min_max() {
173 let mut rs = new_running_stats();
175 rs.add_slice(&[3.0, 1.0, 4.0, 1.0, 5.0]);
176 assert!((rs.min().expect("should succeed") - 1.0).abs() < 1e-12);
177 assert!((rs.max().expect("should succeed") - 5.0).abs() < 1e-12);
178 }
179
180 #[test]
181 fn test_reset() {
182 let mut rs = new_running_stats();
184 rs.add_slice(&[1.0, 2.0, 3.0]);
185 rs.reset();
186 assert_eq!(rs.count(), 0);
187 assert!(rs.min().is_none());
188 }
189
190 #[test]
191 fn test_slice_mean() {
192 assert!((slice_mean(&[10.0, 20.0, 30.0]) - 20.0).abs() < 1e-12);
194 }
195
196 #[test]
197 fn test_slice_variance() {
198 let v = slice_variance(&[1.0, 2.0, 3.0]);
200 assert!((v - 1.0).abs() < 1e-9, "v={v}");
201 }
202}