statrs/statistics/
iter_statistics.rs

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