Skip to main content

incr_stats/
incr.rs

1use crate::error::{Result, StatsError};
2
3#[derive(Default)]
4pub struct Stats {
5    n_int: u32, // Maintain the size as an int to avoid frequent casting.
6    n: f64,
7    min: f64,
8    max: f64,
9    sum: f64,
10    mean: f64,
11    m2: f64,
12    m3: f64,
13    m4: f64,
14}
15
16impl Stats {
17    pub fn new() -> Self {
18        Stats {
19            ..Default::default()
20        }
21    }
22
23    // Update the moments with the given value.
24    pub fn update(&mut self, x: f64) -> Result<()> {
25        if f64::is_nan(x) || f64::is_infinite(x) {
26            return Err(StatsError::InvalidData);
27        }
28        if self.n_int == 0 || x < self.min {
29            self.min = x
30        }
31        if self.n_int == 0 || x > self.max {
32            self.max = x
33        }
34        // Perform incremental updates from the previous values. The updates are done in careful
35        // order; the values used are the prior values until they are updated.
36        self.sum += x;
37        let n_ = self.n; // Prior  n.
38        self.n_int += 1;
39        self.n += 1.0;
40        let delta = x - self.mean; // Deviation from the prior mean.
41        let delta_n = delta / self.n;
42        let delta_n2 = delta_n * delta_n;
43        let term1 = delta * delta_n * n_;
44        // Fourth moment, used to calculate kurtosis.
45        self.m4 += term1 * delta_n2 * (self.n * self.n - 3.0 * self.n + 3.0)
46            + 6.0 * delta_n2 * self.m2
47            - 4.0 * delta_n * self.m3;
48        // Third moment, used to calculate skewness.
49        self.m3 += term1 * delta_n * (self.n - 2.0) - 3.0 * delta_n * self.m2;
50        // Second moment, used to calculate variance.
51        self.m2 += term1;
52        // First moment, the mean.
53        self.mean += delta_n;
54
55        Ok(())
56    }
57
58    pub fn count(&self) -> u32 {
59        self.n_int
60    }
61
62    pub fn min(&self) -> Result<f64> {
63        if self.n_int == 0 {
64            return Err(StatsError::NotEnoughData);
65        }
66        Ok(self.min)
67    }
68
69    pub fn max(&self) -> Result<f64> {
70        if self.n_int == 0 {
71            return Err(StatsError::NotEnoughData);
72        }
73        Ok(self.max)
74    }
75
76    pub fn sum(&self) -> Result<f64> {
77        if self.n_int == 0 {
78            return Err(StatsError::NotEnoughData);
79        }
80        Ok(self.sum)
81    }
82
83    pub fn mean(&self) -> Result<f64> {
84        if self.n_int == 0 {
85            return Err(StatsError::NotEnoughData);
86        }
87        Ok(self.mean)
88    }
89
90    // Update the stats with the given array of values using incremental updates for each value. If
91    // all of the data is contained in a single array, the batch functions below would be faster.
92    // However, this function allows incremental updates with more than one value at a time.
93    pub fn array_update(&mut self, data: &[f64]) -> Result<()> {
94        for v in data {
95            self.update(*v)?;
96        }
97        Ok(())
98    }
99
100    // Population variance:
101    // R: var.pop=function(x){(length(x)-1)/length(x)*var(x)}
102    // Octave: var(a, 1)
103    pub fn population_variance(&self) -> Result<f64> {
104        if self.n_int == 0 || self.n_int == 1 {
105            return Err(StatsError::NotEnoughData);
106        }
107        Ok(self.m2 / self.n)
108    }
109
110    // Sample variance:
111    // R: var(a)
112    // Octave: var(a)
113    pub fn sample_variance(&self) -> Result<f64> {
114        if self.n_int == 0 || self.n_int == 1 {
115            return Err(StatsError::NotEnoughData);
116        }
117        Ok(self.m2 / (self.n - 1.0))
118    }
119
120    // Population standard deviation:
121    // R: sd.pop=function(x){sd(x)*sqrt((length(x)-1)/length(x))}
122    // Octave: std(a, 1)
123    pub fn population_standard_deviation(&self) -> Result<f64> {
124        if self.n_int == 0 || self.n_int == 1 {
125            return Err(StatsError::NotEnoughData);
126        }
127        Ok(f64::sqrt(self.population_variance()?))
128    }
129
130    // Sample standard deviation:
131    // R: sd(a)
132    // Octave: std(a)
133    pub fn sample_standard_deviation(&self) -> Result<f64> {
134        if self.n_int <= 1 {
135            return Err(StatsError::NotEnoughData);
136        }
137        Ok(f64::sqrt(self.sample_variance()?))
138    }
139
140    // Population skewness:
141    // R: library(moments); skewness(a)
142    // or library(DescTools); Skew(a, method = 1)
143    // Octave: skewness(a)
144    pub fn population_skewness(&self) -> Result<f64> {
145        if self.n_int <= 1 {
146            return Err(StatsError::NotEnoughData);
147        }
148        if self.m2 == 0.0 {
149            return Err(StatsError::Undefined);
150        }
151        Ok(f64::sqrt(self.n / (self.m2 * self.m2 * self.m2)) * self.m3)
152    }
153
154    // Sample skewness:
155    // R: library(DescTools); Skew(a, method=2)
156    // Octave: skewness(a, 0)
157    pub fn sample_skewness(&self) -> Result<f64> {
158        if self.n_int <= 2 {
159            return Err(StatsError::NotEnoughData);
160        }
161        Ok(f64::sqrt(self.n * (self.n - 1.0)) / (self.n - 2.0) * self.population_skewness()?)
162    }
163
164    // Population kurtosis:
165    // The kurtosis functions return _excess_ kurtosis.
166    //
167    // Interpretation: kurtosis < 0.0 indicates platykurtic (flat) while kurtosis > 0.0 indicates
168    // leptokurtic (peaked) and near 0 indicates mesokurtic (normal).
169    //
170    // R: library(moments); kurtosis(a) - 3.0 (excess kurtosis)
171    // or library(DescTools); Kurt(a, method = 1)
172    // Octave: kurtosis(a) - 3.0
173    pub fn population_kurtosis(&self) -> Result<f64> {
174        if self.n_int <= 1 {
175            return Err(StatsError::NotEnoughData);
176        }
177        if self.m2 == 0.0 {
178            return Err(StatsError::Undefined);
179        }
180        let k = (self.n * self.m4) / (self.m2 * self.m2) - 3.0;
181        Ok(k)
182    }
183
184    // Sample kurtosis:
185    // R: library(DescTools); Kurt(a, method = 2)
186    // Octave: kurtosis(a, 0) - 3.0
187    pub fn sample_kurtosis(&self) -> Result<f64> {
188        if self.n_int <= 3 {
189            return Err(StatsError::NotEnoughData);
190        }
191        let k = self.population_kurtosis()?;
192        Ok((self.n - 1.0) / ((self.n - 2.0) * (self.n - 3.0)) * ((self.n + 1.0) * k + 6.0))
193    }
194}