statrs/statistics/
iter_statistics.rs1use 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 #[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}