1use core::fmt::{Debug, Formatter};
10use core::ops::{Add, Div, Mul, Sub};
11use irox_tools::f64::FloatExt;
12
13pub trait StreamingStatistic {
17 type Type;
18
19 fn add_sample(&mut self, sample: Self::Type) -> Self::Type;
21 fn get_last_sample(&self) -> Self::Type;
23 fn get_last_result(&self) -> Self::Type;
25 fn get_num_samples(&self) -> u64;
27}
28
29#[derive(Default, Debug, Clone, Copy)]
32pub struct Mean<Type> {
33 sample_count: u64,
34 last_sample: Option<Type>,
35 last_mean: Option<Type>,
36}
37
38impl<T> Mean<T>
39where
40 T: Copy,
41{
42 pub fn get_mean(&self) -> Option<T> {
43 self.last_mean
44 }
45}
46
47pub type MeanF32 = Mean<f32>;
50pub type MeanF64 = Mean<f64>;
53
54impl<Type> StreamingStatistic for Mean<Type>
55where
56 Type: Sub<Type, Output = Type>
57 + Copy
58 + Div<f64, Output = Type>
59 + Add<Type, Output = Type>
60 + Default,
61{
62 type Type = Type;
63
64 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
65 self.sample_count += 1;
66 self.last_sample = Some(sample);
67 let last = self.last_mean.get_or_insert(sample);
68 let mean = *last + (sample - *last) / self.sample_count as f64;
69 *last = mean;
70 mean
71 }
72
73 fn get_last_sample(&self) -> Self::Type {
74 self.last_sample.unwrap_or_default()
75 }
76
77 fn get_last_result(&self) -> Self::Type {
78 self.last_mean.unwrap_or_default()
79 }
80
81 fn get_num_samples(&self) -> u64 {
82 self.sample_count
83 }
84}
85
86#[derive(Default, Debug, Clone, Copy)]
88pub struct Max<T> {
89 max_val: Option<T>,
90 last_sample: Option<T>,
91 num_samples: u64,
92}
93impl<T: Copy> Max<T> {
94 pub fn get_max_val(&self) -> Option<T> {
95 self.max_val
96 }
97}
98
99impl<T: Default> StreamingStatistic for Max<T>
100where
101 T: PartialOrd + Copy,
102{
103 type Type = T;
104
105 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
106 self.num_samples += 1;
107 let mut max = self.max_val.get_or_insert(sample);
108 if sample.ge(max) {
109 max = self.max_val.insert(sample);
110 }
111 *max
112 }
113
114 fn get_last_sample(&self) -> Self::Type {
115 self.last_sample.unwrap_or_default()
116 }
117
118 fn get_last_result(&self) -> Self::Type {
119 self.max_val.unwrap_or_default()
120 }
121
122 fn get_num_samples(&self) -> u64 {
123 self.num_samples
124 }
125}
126
127#[derive(Default, Debug, Clone, Copy)]
129pub struct Min<T> {
130 min_val: Option<T>,
131 last_sample: Option<T>,
132 num_samples: u64,
133}
134
135impl<T: Copy> Min<T> {
136 pub fn get_min_val(&self) -> Option<T> {
137 self.min_val
138 }
139}
140
141impl<T: Default> StreamingStatistic for Min<T>
142where
143 T: PartialOrd + Copy,
144{
145 type Type = T;
146
147 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
148 self.num_samples += 1;
149 let mut min = self.min_val.get_or_insert(sample);
150 if sample.le(min) {
151 min = self.min_val.insert(sample);
152 }
153 *min
154 }
155
156 fn get_last_sample(&self) -> Self::Type {
157 self.last_sample.unwrap_or_default()
158 }
159
160 fn get_last_result(&self) -> Self::Type {
161 self.min_val.unwrap_or_default()
162 }
163
164 fn get_num_samples(&self) -> u64 {
165 self.num_samples
166 }
167}
168
169#[derive(Default, Debug, Clone, Copy)]
173pub struct UnweightedSumOfSquares<T> {
174 means: Mean<T>,
175 last_ssq: Option<T>,
176}
177
178impl<T> UnweightedSumOfSquares<T>
179where
180 T: Copy + Default,
181{
182 pub fn get_mean(&self) -> T {
183 self.means.last_mean.unwrap_or_default()
184 }
185 pub fn get_unweighted_sum_of_squares(&self) -> T {
186 self.last_ssq.unwrap_or_default()
187 }
188}
189
190impl<Type> StreamingStatistic for UnweightedSumOfSquares<Type>
191where
192 Type: Sub<Type, Output = Type>
193 + Copy
194 + Default
195 + Div<f64, Output = Type>
196 + Add<Type, Output = Type>
197 + Mul<f64, Output = Type>
198 + Mul<Type, Output = Type>,
199{
200 type Type = Type;
201
202 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
203 let count = self.means.get_num_samples() as f64;
204 let var = sample - self.means.get_last_result();
205 let last = self.last_ssq.unwrap_or_default();
206 let ussq = last + var * (var / (count + 1.0)) * count;
207 self.last_ssq = Some(ussq);
208 let _ = self.means.add_sample(sample);
209 ussq
210 }
211
212 fn get_last_sample(&self) -> Self::Type {
213 self.means.get_last_sample()
214 }
215
216 fn get_last_result(&self) -> Self::Type {
217 self.last_ssq.unwrap_or_default()
218 }
219
220 fn get_num_samples(&self) -> u64 {
221 self.means.get_num_samples()
222 }
223}
224
225#[derive(Default, Debug, Copy, Clone)]
229pub struct BiasedVariance<T> {
230 inner: UnweightedSumOfSquares<T>,
231 last_result: Option<T>,
232}
233
234impl<T> BiasedVariance<T>
235where
236 T: Copy + Default,
237{
238 pub fn get_mean(&self) -> T {
239 self.inner.get_mean()
240 }
241 pub fn get_unweighted_sum_of_squares(&self) -> Option<T> {
242 self.inner.last_ssq
243 }
244 pub fn get_biased_variance(&self) -> Option<T> {
245 self.last_result
246 }
247}
248
249impl<T> StreamingStatistic for BiasedVariance<T>
250where
251 T: Sub<T, Output = T>
252 + Copy
253 + Default
254 + Div<f64, Output = T>
255 + Add<T, Output = T>
256 + Mul<f64, Output = T>
257 + Mul<T, Output = T>,
258{
259 type Type = T;
260
261 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
262 let cnt = self.get_num_samples() + 1; let variance = self.inner.add_sample(sample) / cnt as f64;
264 self.last_result = Some(variance);
265 variance
266 }
267
268 fn get_last_sample(&self) -> Self::Type {
269 self.inner.get_last_sample()
270 }
271
272 fn get_last_result(&self) -> Self::Type {
273 self.last_result.unwrap_or_default()
274 }
275
276 fn get_num_samples(&self) -> u64 {
277 self.inner.get_num_samples()
278 }
279}
280
281#[derive(Default, Debug, Copy, Clone)]
285pub struct UnbiasedVariance<T> {
286 inner: UnweightedSumOfSquares<T>,
287 last_result: Option<T>,
288}
289
290impl<T> UnbiasedVariance<T>
291where
292 T: Copy + Default,
293{
294 pub fn get_mean(&self) -> T {
295 self.inner.get_mean()
296 }
297 pub fn get_unweighted_sum_of_squares(&self) -> Option<T> {
298 self.inner.last_ssq
299 }
300 pub fn get_unbiased_variance(&self) -> Option<T> {
301 self.last_result
302 }
303}
304
305impl<T> StreamingStatistic for UnbiasedVariance<T>
306where
307 T: Sub<T, Output = T>
308 + Copy
309 + Default
310 + Div<f64, Output = T>
311 + Add<T, Output = T>
312 + Mul<f64, Output = T>
313 + Mul<T, Output = T>,
314{
315 type Type = T;
316
317 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
318 let cnt = self.get_num_samples();
319 let variance = self.inner.add_sample(sample) / cnt as f64;
320 self.last_result = Some(variance);
321 variance
322 }
323
324 fn get_last_sample(&self) -> Self::Type {
325 self.inner.get_last_sample()
326 }
327
328 fn get_last_result(&self) -> Self::Type {
329 self.last_result.unwrap_or_default()
330 }
331
332 fn get_num_samples(&self) -> u64 {
333 self.inner.get_num_samples()
334 }
335}
336
337#[derive(Default, Debug, Copy, Clone)]
341pub struct UnbiasedStandardDeviation<T> {
342 inner: UnbiasedVariance<T>,
343 last_result: Option<T>,
344}
345
346impl<T> UnbiasedStandardDeviation<T>
347where
348 T: Copy + Default,
349{
350 pub fn get_mean(&self) -> T {
351 self.inner.get_mean()
352 }
353 pub fn get_unweighted_sum_of_squares(&self) -> Option<T> {
354 self.inner.get_unweighted_sum_of_squares()
355 }
356 pub fn get_unbiased_variance(&self) -> Option<T> {
357 self.inner.last_result
358 }
359 pub fn get_unbiased_stdev(&self) -> Option<T> {
360 self.last_result
361 }
362}
363
364impl<T> StreamingStatistic for UnbiasedStandardDeviation<T>
365where
366 T: Sub<T, Output = T>
367 + Copy
368 + Default
369 + Div<f64, Output = T>
370 + Add<T, Output = T>
371 + Mul<f64, Output = T>
372 + Mul<T, Output = T>
373 + FloatExt<Type = T>,
374{
375 type Type = T;
376
377 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
378 let val = self.inner.add_sample(sample).sqrt();
379 self.last_result = Some(val);
380 val
381 }
382
383 fn get_last_sample(&self) -> Self::Type {
384 self.inner.get_last_sample()
385 }
386
387 fn get_last_result(&self) -> Self::Type {
388 self.last_result.unwrap_or_default()
389 }
390
391 fn get_num_samples(&self) -> u64 {
392 self.inner.get_num_samples()
393 }
394}
395
396#[derive(Default, Debug, Copy, Clone)]
400pub struct BiasedStandardDeviation<T> {
401 inner: BiasedVariance<T>,
402 last_result: Option<T>,
403}
404
405impl<T> BiasedStandardDeviation<T>
406where
407 T: Copy + Default,
408{
409 pub fn get_mean(&self) -> T {
410 self.inner.get_mean()
411 }
412 pub fn get_unweighted_sum_of_squares(&self) -> Option<T> {
413 self.inner.get_unweighted_sum_of_squares()
414 }
415 pub fn get_biased_variance(&self) -> Option<T> {
416 self.inner.get_biased_variance()
417 }
418 pub fn get_biased_stdev(&self) -> Option<T> {
419 self.last_result
420 }
421}
422
423impl<T> StreamingStatistic for BiasedStandardDeviation<T>
424where
425 T: Sub<T, Output = T>
426 + Copy
427 + Default
428 + Div<f64, Output = T>
429 + Add<T, Output = T>
430 + Mul<f64, Output = T>
431 + Mul<T, Output = T>
432 + FloatExt<Type = T>,
433{
434 type Type = T;
435
436 fn add_sample(&mut self, sample: Self::Type) -> Self::Type {
437 let val = self.inner.add_sample(sample).sqrt();
438 self.last_result = Some(val);
439 val
440 }
441
442 fn get_last_sample(&self) -> Self::Type {
443 self.inner.get_last_sample()
444 }
445
446 fn get_last_result(&self) -> Self::Type {
447 self.last_result.unwrap_or_default()
448 }
449
450 fn get_num_samples(&self) -> u64 {
451 self.inner.get_num_samples()
452 }
453}
454
455#[derive(Copy, Clone)]
456pub struct Summary<T> {
457 mean: Mean<T>,
458 min: Min<T>,
459 max: Max<T>,
460 stdev: UnbiasedStandardDeviation<T>,
461}
462impl<T: Default> Default for Summary<T> {
463 fn default() -> Self {
464 Summary {
465 mean: Mean::default(),
466 min: Min::default(),
467 max: Max::default(),
468 stdev: UnbiasedStandardDeviation::default(),
469 }
470 }
471}
472impl<T: Debug + Copy + Default> Debug for Summary<T> {
473 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
474 f.write_fmt(format_args!(
475 "avg: {:?} +/- {:?} (1std) [{:?}-{:?}]",
476 self.mean.get_mean(),
477 self.stdev.get_unbiased_stdev(),
478 self.min.get_min_val(),
479 self.max.get_max_val()
480 ))
481 }
482}
483impl<T> Summary<T>
484where
485 T: Sub<T, Output = T>
486 + PartialOrd
487 + Copy
488 + Default
489 + Div<f64, Output = T>
490 + Add<T, Output = T>
491 + Mul<f64, Output = T>
492 + Mul<T, Output = T>
493 + FloatExt<Type = T>,
494{
495 pub fn add_sample(&mut self, value: T) {
496 self.min.add_sample(value);
497 self.max.add_sample(value);
498 self.stdev.add_sample(value);
499 self.mean.add_sample(value);
500 }
501 #[must_use]
502 pub fn mean(&self) -> Option<T> {
503 self.mean.get_mean()
504 }
505 #[must_use]
506 pub fn min(&self) -> Option<T> {
507 self.min.get_min_val()
508 }
509 #[must_use]
510 pub fn max(&self) -> Option<T> {
511 self.max.get_max_val()
512 }
513 #[must_use]
514 pub fn stddev(&self) -> Option<T> {
515 self.stdev.get_unbiased_stdev()
516 }
517 #[must_use]
518 pub fn num_samples(&self) -> u64 {
519 self.mean.get_num_samples()
520 }
521}
522#[cfg(feature = "std")]
523pub struct OneSecondWindows {
524 epoch: irox_time::epoch::Epoch,
525 windows: alloc::collections::BTreeMap<irox_time::Time64, Summary<f64>>,
526}
527#[cfg(feature = "std")]
528impl OneSecondWindows {
529 pub fn new(epoch: irox_time::epoch::Epoch) -> Self {
530 Self {
531 epoch,
532 windows: alloc::collections::BTreeMap::new(),
533 }
534 }
535 pub fn add_sample(&mut self, time: irox_time::Time64, value: f64) {
536 let seconds = time.as_epoch(self.epoch).as_only_seconds();
537 self.windows.entry(seconds).or_default().add_sample(value);
538 }
539
540 pub fn iter(&self) -> impl Iterator<Item = (&irox_time::Time64, &Summary<f64>)> {
541 self.windows.iter()
542 }
543}
544
545#[cfg(test)]
546mod test {
547 use crate::streaming::*;
548 use irox_tools::assert_eq_eps;
549 use irox_units::units::duration::Duration;
550
551 #[test]
552 pub fn test() {
553 let mut mean = MeanF64::default();
554 let v = mean.add_sample(0.0);
555 assert_eq!(v, 0.0);
556 let v = mean.add_sample(1.0);
557 assert_eq!(v, 0.5);
558
559 let mut mean = Mean::<Duration>::default();
560 let v = mean.add_sample(Duration::from_seconds(1));
561 assert_eq!(v, Duration::from_seconds_f64(1.0));
562 let v = mean.add_sample(Duration::from_seconds(2));
563 assert_eq!(v, Duration::from_seconds_f64(1.5));
564
565 let mut samp_stddev = UnbiasedStandardDeviation::default();
566 let mut pop_stddev = BiasedStandardDeviation::default();
567 for val in [2, 4, 4, 4, 5, 5, 7, 9] {
568 let _ = samp_stddev.add_sample(val as f64);
569 let _ = pop_stddev.add_sample(val as f64);
570 }
571 assert_eq!(5.0, samp_stddev.get_mean());
572 assert_eq_eps!(
573 4.571428571428571,
574 samp_stddev.get_unbiased_variance().unwrap_or_default(),
575 1e-15
576 );
577 assert_eq_eps!(
578 2.1380899352993947,
579 samp_stddev.get_unbiased_stdev().unwrap_or_default(),
580 1e-15
581 );
582
583 assert_eq!(5.0, pop_stddev.get_mean());
584 assert_eq_eps!(
585 4.,
586 pop_stddev.get_biased_variance().unwrap_or_default(),
587 1e-15
588 );
589 assert_eq_eps!(2., pop_stddev.get_biased_stdev().unwrap_or_default(), 1e-15);
590 }
591}