Skip to main content

statrs/statistics/
iter_statistics.rs

1use crate::statistics::*;
2use core::borrow::Borrow;
3#[cfg(not(feature = "std"))]
4use num_traits::Float as _;
5
6impl<T> Statistics<f64> for T
7where
8    T: IntoIterator,
9    T::Item: Borrow<f64>,
10{
11    fn min(self) -> f64 {
12        let mut iter = self.into_iter();
13        match iter.next() {
14            None => f64::NAN,
15            Some(x) => iter.map(|x| *x.borrow()).fold(*x.borrow(), |acc, x| {
16                if x < acc || x.is_nan() { x } else { acc }
17            }),
18        }
19    }
20
21    fn max(self) -> f64 {
22        let mut iter = self.into_iter();
23        match iter.next() {
24            None => f64::NAN,
25            Some(x) => iter.map(|x| *x.borrow()).fold(*x.borrow(), |acc, x| {
26                if x > acc || x.is_nan() { x } else { acc }
27            }),
28        }
29    }
30
31    fn abs_min(self) -> f64 {
32        let mut iter = self.into_iter();
33        match iter.next() {
34            None => f64::NAN,
35            Some(init) => iter
36                .map(|x| x.borrow().abs())
37                .fold(init.borrow().abs(), |acc, x| {
38                    if x < acc || x.is_nan() { x } else { acc }
39                }),
40        }
41    }
42
43    fn abs_max(self) -> f64 {
44        let mut iter = self.into_iter();
45        match iter.next() {
46            None => f64::NAN,
47            Some(init) => iter
48                .map(|x| x.borrow().abs())
49                .fold(init.borrow().abs(), |acc, x| {
50                    if x > acc || x.is_nan() { x } else { acc }
51                }),
52        }
53    }
54
55    fn mean(self) -> f64 {
56        self.into_iter()
57            .fold(OnlineMoments::<2>::default(), |acc, x| {
58                acc.push(*x.borrow())
59            })
60            .mean()
61            .unwrap_or(f64::NAN)
62    }
63
64    fn geometric_mean(self) -> f64 {
65        let mut i = 0.0;
66        let mut sum = 0.0;
67        for x in self {
68            i += 1.0;
69            sum += x.borrow().ln();
70        }
71        if i > 0.0 { (sum / i).exp() } else { f64::NAN }
72    }
73
74    fn harmonic_mean(self) -> f64 {
75        let mut i = 0.0;
76        let mut sum = 0.0;
77        for x in self {
78            i += 1.0;
79
80            let borrow = *x.borrow();
81            if borrow < 0f64 {
82                return f64::NAN;
83            }
84            sum += 1.0 / borrow;
85        }
86        if i > 0.0 { i / sum } else { f64::NAN }
87    }
88
89    fn variance(self) -> f64 {
90        self.into_iter()
91            .fold(OnlineMoments::<2>::default(), |acc, x| {
92                acc.push(*x.borrow())
93            })
94            .variance()
95            .unwrap_or(f64::NAN)
96    }
97
98    fn std_dev(self) -> f64 {
99        self.variance().sqrt()
100    }
101
102    fn population_variance(self) -> f64 {
103        self.into_iter()
104            .fold(OnlineMoments::<2>::default(), |acc, x| {
105                acc.push(*x.borrow())
106            })
107            .population_variance()
108            .unwrap_or(f64::NAN)
109    }
110
111    fn population_std_dev(self) -> f64 {
112        self.population_variance().sqrt()
113    }
114
115    fn covariance(self, other: Self) -> f64 {
116        let mut n = 0.0;
117        let mut mean1 = 0.0;
118        let mut mean2 = 0.0;
119        let mut comoment = 0.0;
120
121        let mut iter = other.into_iter();
122        for x in self {
123            let borrow = *x.borrow();
124            let borrow2 = match iter.next() {
125                None => panic!("Iterators must have the same length"),
126                Some(x) => *x.borrow(),
127            };
128            let old_mean2 = mean2;
129            n += 1.0;
130            mean1 += (borrow - mean1) / n;
131            mean2 += (borrow2 - mean2) / n;
132            comoment += (borrow - mean1) * (borrow2 - old_mean2);
133        }
134        if iter.next().is_some() {
135            panic!("Iterators must have the same length");
136        }
137
138        if n > 1.0 {
139            comoment / (n - 1.0)
140        } else {
141            f64::NAN
142        }
143    }
144
145    fn population_covariance(self, other: Self) -> f64 {
146        let mut n = 0.0;
147        let mut mean1 = 0.0;
148        let mut mean2 = 0.0;
149        let mut comoment = 0.0;
150
151        let mut iter = other.into_iter();
152        for x in self {
153            let borrow = *x.borrow();
154            let borrow2 = match iter.next() {
155                None => panic!("Iterators must have the same length"),
156                Some(x) => *x.borrow(),
157            };
158            let old_mean2 = mean2;
159            n += 1.0;
160            mean1 += (borrow - mean1) / n;
161            mean2 += (borrow2 - mean2) / n;
162            comoment += (borrow - mean1) * (borrow2 - old_mean2);
163        }
164        if iter.next().is_some() {
165            panic!("Iterators must have the same length")
166        }
167        if n > 0.0 { comoment / n } else { f64::NAN }
168    }
169
170    fn quadratic_mean(self) -> f64 {
171        let mut i = 0.0;
172        let mut mean = 0.0;
173        for x in self {
174            let borrow = *x.borrow();
175            i += 1.0;
176            mean += (borrow * borrow - mean) / i;
177        }
178        if i > 0.0 { mean.sqrt() } else { f64::NAN }
179    }
180}
181
182#[rustfmt::skip]
183#[cfg(test)]
184mod tests {
185    use core::f64::consts;
186    use crate::generate::{InfinitePeriodic, InfiniteSinusoidal};
187    use crate::prec;
188    use crate::statistics::Statistics;
189
190    #[test]
191    fn test_empty_data_returns_nan() {
192        let data = [0.0; 0];
193        assert!(data.min().is_nan());
194        assert!(data.max().is_nan());
195        assert!(data.mean().is_nan());
196        assert!(data.quadratic_mean().is_nan());
197        assert!(data.variance().is_nan());
198        assert!(data.population_variance().is_nan());
199    }
200
201    // TODO: test github issue 137 (Math.NET)
202
203    #[test]
204    fn test_large_samples() {
205        let shorter = || InfinitePeriodic::default(4.0, 1.0).take(4*4096);
206        let longer = || InfinitePeriodic::default(4.0, 1.0).take(4*32768);
207        let s_mean = shorter().mean();
208        let s_qmean = shorter().quadratic_mean();
209        let l_mean = longer().mean();
210        let l_qmean = longer().quadratic_mean();
211
212        prec::assert_abs_diff_eq!(s_mean, 0.375, epsilon = 1e-14);
213        prec::assert_abs_diff_eq!(l_mean, 0.375, epsilon = 1e-14);
214        prec::assert_abs_diff_eq!(s_qmean, (0.21875f64).sqrt(), epsilon = 1e-14);
215        prec::assert_abs_diff_eq!(l_qmean, (0.21875f64).sqrt(), epsilon = 1e-14);
216    }
217
218    #[test]
219    fn test_quadratic_mean_of_sinusoidal() {
220        let data = InfiniteSinusoidal::default(64.0, 16.0, 2.0).take(128);
221        let qmean = data.quadratic_mean();
222
223        prec::assert_abs_diff_eq!(qmean, 2.0 / consts::SQRT_2, epsilon = 1e-15);
224    }
225}