flowstats/statistics/
moments.rs1use crate::math;
7use crate::traits::{MergeError, Sketch};
8
9#[derive(Clone, Debug)]
57pub struct RunningStats {
58 count: u64,
60 mean: f64,
62 m2: f64,
64 min: f64,
66 max: f64,
68}
69
70impl Default for RunningStats {
71 fn default() -> Self {
72 Self::new()
73 }
74}
75
76impl RunningStats {
77 pub fn new() -> Self {
79 Self {
80 count: 0,
81 mean: 0.0,
82 m2: 0.0,
83 min: f64::INFINITY,
84 max: f64::NEG_INFINITY,
85 }
86 }
87
88 pub fn add(&mut self, value: f64) {
93 if value.is_nan() {
95 return;
96 }
97
98 self.count += 1;
99
100 if value < self.min {
102 self.min = value;
103 }
104 if value > self.max {
105 self.max = value;
106 }
107
108 let delta = value - self.mean;
110 self.mean += delta / self.count as f64;
111 let delta2 = value - self.mean;
112 self.m2 += delta * delta2;
113 }
114
115 pub fn len(&self) -> u64 {
117 self.count
118 }
119
120 pub fn is_empty(&self) -> bool {
122 self.count == 0
123 }
124
125 pub fn mean(&self) -> f64 {
127 if self.count == 0 {
128 0.0
129 } else {
130 self.mean
131 }
132 }
133
134 pub fn variance(&self) -> f64 {
139 if self.count < 1 {
140 0.0
141 } else {
142 self.m2 / self.count as f64
143 }
144 }
145
146 pub fn sample_variance(&self) -> f64 {
151 if self.count < 2 {
152 0.0
153 } else {
154 self.m2 / (self.count - 1) as f64
155 }
156 }
157
158 pub fn stddev(&self) -> f64 {
160 math::sqrt(self.variance())
161 }
162
163 pub fn sample_stddev(&self) -> f64 {
165 math::sqrt(self.sample_variance())
166 }
167
168 pub fn min(&self) -> Option<f64> {
170 if self.count == 0 {
171 None
172 } else {
173 Some(self.min)
174 }
175 }
176
177 pub fn max(&self) -> Option<f64> {
179 if self.count == 0 {
180 None
181 } else {
182 Some(self.max)
183 }
184 }
185
186 pub fn range(&self) -> Option<f64> {
188 if self.count == 0 {
189 None
190 } else {
191 Some(self.max - self.min)
192 }
193 }
194
195 pub fn sum(&self) -> f64 {
197 self.mean * self.count as f64
198 }
199
200 pub fn merge_stats(&mut self, other: &Self) {
204 if other.count == 0 {
205 return;
206 }
207
208 if self.count == 0 {
209 *self = other.clone();
210 return;
211 }
212
213 let combined_count = self.count + other.count;
214 let delta = other.mean - self.mean;
215
216 let combined_mean = self.mean + delta * (other.count as f64 / combined_count as f64);
218
219 let combined_m2 = self.m2
221 + other.m2
222 + delta * delta * (self.count as f64 * other.count as f64 / combined_count as f64);
223
224 self.count = combined_count;
226 self.mean = combined_mean;
227 self.m2 = combined_m2;
228 self.min = self.min.min(other.min);
229 self.max = self.max.max(other.max);
230 }
231}
232
233impl Sketch for RunningStats {
234 type Item = f64;
235
236 fn update(&mut self, item: &Self::Item) {
237 self.add(*item);
238 }
239
240 fn merge(&mut self, other: &Self) -> Result<(), MergeError> {
241 self.merge_stats(other);
242 Ok(())
243 }
244
245 fn clear(&mut self) {
246 *self = Self::new();
247 }
248
249 fn size_bytes(&self) -> usize {
250 core::mem::size_of::<Self>()
251 }
252
253 fn count(&self) -> u64 {
254 self.count
255 }
256}
257
258#[cfg(test)]
259mod tests {
260 use super::*;
261
262 #[test]
263 fn test_basic() {
264 let mut stats = RunningStats::new();
265
266 stats.add(2.0);
267 stats.add(4.0);
268 stats.add(4.0);
269 stats.add(4.0);
270 stats.add(5.0);
271 stats.add(5.0);
272 stats.add(7.0);
273 stats.add(9.0);
274
275 assert_eq!(stats.len(), 8);
276 assert!((stats.mean() - 5.0).abs() < 0.001);
277 assert!((stats.variance() - 4.0).abs() < 0.001);
278 assert!((stats.stddev() - 2.0).abs() < 0.001);
279 assert_eq!(stats.min(), Some(2.0));
280 assert_eq!(stats.max(), Some(9.0));
281 }
282
283 #[test]
284 fn test_single_value() {
285 let mut stats = RunningStats::new();
286 stats.add(42.0);
287
288 assert_eq!(stats.len(), 1);
289 assert!((stats.mean() - 42.0).abs() < 0.001);
290 assert!((stats.variance() - 0.0).abs() < 0.001);
291 assert_eq!(stats.min(), Some(42.0));
292 assert_eq!(stats.max(), Some(42.0));
293 }
294
295 #[test]
296 fn test_empty() {
297 let stats = RunningStats::new();
298
299 assert!(stats.is_empty());
300 assert_eq!(stats.mean(), 0.0);
301 assert_eq!(stats.variance(), 0.0);
302 assert_eq!(stats.min(), None);
303 assert_eq!(stats.max(), None);
304 }
305
306 #[test]
307 fn test_merge() {
308 let mut stats1 = RunningStats::new();
309 let mut stats2 = RunningStats::new();
310
311 for v in [1.0, 2.0, 3.0] {
313 stats1.add(v);
314 }
315 for v in [4.0, 5.0, 6.0] {
316 stats2.add(v);
317 }
318
319 stats1.merge(&stats2).unwrap();
320
321 assert_eq!(stats1.len(), 6);
322 assert!((stats1.mean() - 3.5).abs() < 0.001);
323 assert_eq!(stats1.min(), Some(1.0));
324 assert_eq!(stats1.max(), Some(6.0));
325 assert!((stats1.sum() - 21.0).abs() < 0.001);
326 }
327
328 #[test]
329 fn test_merge_empty() {
330 let mut stats1 = RunningStats::new();
331 let stats2 = RunningStats::new();
332
333 stats1.add(1.0);
334 stats1.add(2.0);
335
336 stats1.merge(&stats2).unwrap();
337
338 assert_eq!(stats1.len(), 2);
339 assert!((stats1.mean() - 1.5).abs() < 0.001);
340 }
341
342 #[test]
343 fn test_sample_variance() {
344 let mut stats = RunningStats::new();
345
346 for v in [2.0, 4.0, 4.0, 4.0, 5.0, 5.0, 7.0, 9.0] {
351 stats.add(v);
352 }
353
354 assert!((stats.variance() - 4.0).abs() < 0.001);
356
357 assert!((stats.sample_variance() - 4.571).abs() < 0.01);
359 }
360
361 #[test]
362 fn test_clear() {
363 let mut stats = RunningStats::new();
364
365 stats.add(1.0);
366 stats.add(2.0);
367 stats.add(3.0);
368
369 stats.clear();
370
371 assert!(stats.is_empty());
372 assert_eq!(stats.min(), None);
373 }
374
375 #[test]
376 fn test_numerical_stability() {
377 let mut stats = RunningStats::new();
379
380 let base = 1e12;
381 for i in 0..1000 {
382 stats.add(base + i as f64);
383 }
384
385 let expected_mean = base + 499.5;
387 assert!(
388 (stats.mean() - expected_mean).abs() < 1.0,
389 "Mean: {} expected: {}",
390 stats.mean(),
391 expected_mean
392 );
393 }
394
395 #[test]
396 fn test_nan_ignored() {
397 let mut stats = RunningStats::new();
398
399 stats.add(1.0);
400 stats.add(f64::NAN);
401 stats.add(2.0);
402 stats.add(f64::NAN);
403 stats.add(3.0);
404
405 assert_eq!(stats.len(), 3);
407 assert!((stats.mean() - 2.0).abs() < 0.001);
408 assert_eq!(stats.min(), Some(1.0));
409 assert_eq!(stats.max(), Some(3.0));
410
411 assert!(!stats.mean().is_nan());
413 assert!(!stats.variance().is_nan());
414 }
415
416 #[test]
417 fn test_infinity() {
418 let mut stats = RunningStats::new();
419
420 stats.add(1.0);
421 stats.add(f64::INFINITY);
422 stats.add(2.0);
423
424 assert_eq!(stats.len(), 3);
426 assert_eq!(stats.max(), Some(f64::INFINITY));
427 }
428}