1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
//! Streaming moments: count, mean, variance, and extrema in one pass.
/// Running statistics over a stream of values: Welford's online algorithm for the
/// mean and variance, plus the extrema.
///
/// A mergeable monoid: partial results over chunks combine with [`Moments::merge`]
/// (Chan's parallel formula) into the same statistics a single pass produces.
/// Non-finite values are ignored.
#[derive(Debug, Clone, Copy)]
pub struct Moments {
count: u64,
mean: f64,
m2: f64,
min: f64,
max: f64,
}
impl Moments {
/// An empty accumulator.
pub fn new() -> Moments {
Moments {
count: 0,
mean: 0.0,
m2: 0.0,
min: f64::INFINITY,
max: f64::NEG_INFINITY,
}
}
/// Accumulates one value; non-finite values are ignored.
pub fn add(&mut self, value: f64) {
if !value.is_finite() {
return;
}
self.count += 1;
let delta = value - self.mean;
self.mean += delta / self.count as f64;
self.m2 += delta * (value - self.mean);
self.min = self.min.min(value);
self.max = self.max.max(value);
}
/// Merges another accumulator into this one.
pub fn merge(&mut self, other: &Moments) {
if other.count == 0 {
return;
}
if self.count == 0 {
*self = *other;
return;
}
let total = self.count + other.count;
let delta = other.mean - self.mean;
self.mean += delta * other.count as f64 / total as f64;
self.m2 += other.m2 + delta * delta * self.count as f64 * other.count as f64 / total as f64;
self.count = total;
self.min = self.min.min(other.min);
self.max = self.max.max(other.max);
}
/// The number of finite values seen.
pub fn count(&self) -> u64 {
self.count
}
/// The mean, or `None` before any value.
pub fn mean(&self) -> Option<f64> {
(self.count > 0).then_some(self.mean)
}
/// The population variance (`n` in the denominator), or `None` before any
/// value.
pub fn variance(&self) -> Option<f64> {
(self.count > 0).then(|| self.m2 / self.count as f64)
}
/// The population standard deviation, or `None` before any value.
pub fn standard_deviation(&self) -> Option<f64> {
self.variance().map(f64::sqrt)
}
/// The sample variance (`n − 1` in the denominator — the unbiased
/// estimate pandas and R report), or `None` below two values.
pub fn sample_variance(&self) -> Option<f64> {
(self.count > 1).then(|| self.m2 / (self.count - 1) as f64)
}
/// The sample standard deviation, or `None` below two values — what
/// [`Reducer::Deviation`](super::Reducer::Deviation) reduces to.
pub fn sample_standard_deviation(&self) -> Option<f64> {
self.sample_variance().map(f64::sqrt)
}
/// The smallest value, or `None` before any value.
pub fn min(&self) -> Option<f64> {
(self.count > 0).then_some(self.min)
}
/// The largest value, or `None` before any value.
pub fn max(&self) -> Option<f64> {
(self.count > 0).then_some(self.max)
}
}
impl Default for Moments {
/// The empty accumulator — identical to [`Moments::new`]. Derived `Default`
/// would start the extrema at `0`, which corrupts min/max on the first value.
fn default() -> Moments {
Moments::new()
}
}
#[cfg(test)]
#[path = "tests/moments_tests.rs"]
mod tests;