vec_utilities/maths/
stats.rs

1/// NOTE: if there are any NaNs in the Iterator then this trait will prduce
2/// unstable results
3pub trait Statistics<T> {
4    fn mean(self) -> Option<T>;
5    fn median(self) -> Option<T>;
6    fn variance(self) -> Option<T>;
7    fn std(self) -> Option<T>;
8
9    // Called them float_max and float_min so that there's no conflicts
10    fn float_max(self) -> T;
11    fn float_min(self) -> T;
12
13    fn difference(self) -> T;
14    fn zero_crossings(self) -> usize;
15
16    fn peak_average_ratio(self) -> Option<T>;
17}
18
19macro_rules! impl_stats_iterator {
20    ($float:ty) => {
21        impl<'a, T: Iterator<Item = &'a $float>> Statistics<$float> for T {
22            fn mean(self) -> Option<$float> {
23                let (sum, count) =
24                    self.fold((0.0, 0), |(sum, count), item| (sum + item, count + 1));
25
26                if count > 0 {
27                    Some(sum / (count as $float))
28                } else {
29                    None
30                }
31            }
32            //TODO! Population implemented here. Sample may be wanted?
33            fn variance(self) -> Option<$float> {
34                let mut m = 0.0 as $float;
35                let mut s = 0.0 as $float;
36                let mut k = 1.0 as $float;
37
38                for item in self {
39                    let old_m = m;
40                    m = m + (item - m) / k;
41                    s = s + (item - m) * (item - old_m);
42                    k = k + 1.0 as $float;
43                }
44
45                if k > 0.0 as $float {
46                    return Some(s / (k - 1.0 as $float));
47                } else {
48                    return None;
49                }
50            }
51
52            fn std(self) -> Option<$float> {
53                self.variance().map(|x| x.sqrt())
54            }
55
56            fn median(self) -> Option<$float> {
57                // TODO: Not 100% happy with this implementation due to the cloning but what can you do?
58                let elements: Vec<$float> = self.cloned().collect();
59                let len = elements.len();
60                if elements.len() == 0 {
61                    return None;
62                }
63
64                let mut new = elements
65                    .iter()
66                    .map(|x| x.to_owned())
67                    .collect::<Vec<$float>>();
68
69                new.sort_by(|a, b| a.total_cmp(b));
70
71                let mid = len / 2;
72
73                if len % 2 == 0 {
74                    let low = match new.get(mid - 1) {
75                        Some(x) => x,
76                        None => return None,
77                    };
78
79                    let high = match new.get(mid) {
80                        Some(x) => x,
81                        None => return None,
82                    };
83
84                    return Some((low + high) / (2.0 as $float));
85                } else {
86                    return match new.get(mid) {
87                        Some(x) => Some(*x),
88                        None => None,
89                    };
90                }
91            }
92
93            fn float_max(self) -> $float {
94                self.fold(<$float>::NEG_INFINITY, |a, b| a.max(*b))
95            }
96
97            fn float_min(self) -> $float {
98                self.fold(<$float>::INFINITY, |a, b| a.min(*b))
99            }
100
101            fn difference(self) -> $float {
102                // self.float_max() - self.float_min()
103                // You'd like to do that but it consumes the iterator
104
105                let mut min = <$float>::INFINITY;
106                let mut max = <$float>::NEG_INFINITY;
107
108                for n in self {
109                    min = n.min(min);
110                    max = n.max(max);
111                }
112
113                max - min
114            }
115
116            fn zero_crossings(self) -> usize {
117                let mut zero_crossings = 0;
118                let mut prev_item = None;
119
120                for item in self {
121                    if let Some(prev) = prev_item {
122                        if prev * item < 0.0 {
123                            zero_crossings += 1;
124                        }
125                    }
126                    prev_item = Some(item);
127                }
128
129                zero_crossings
130            }
131
132            fn peak_average_ratio(self) -> Option<$float> {
133                let (sum, count, max) = self.fold(
134                    (0.0, 0, <$float>::NEG_INFINITY),
135                    |(sum, count, max), item| (sum + item, count + 1, item.max(max)),
136                );
137
138                if count == 0 {
139                    return None;
140                } else {
141                    return Some(max / (sum / count as $float));
142                }
143            }
144        }
145    };
146}
147
148impl_stats_iterator!(f64);
149impl_stats_iterator!(f32);