1
|
|
use std::f64;
|
2
|
|
use std::iter::{Skip, Zip};
|
3
|
|
use std::slice::Iter;
|
4
|
|
|
5
|
|
pub trait PairWise<T> {
|
6
|
|
fn pairwise(&self) -> Zip<Iter<T>, Skip<Iter<T>>>;
|
7
|
|
}
|
8
|
|
|
9
|
|
impl<T> PairWise<T> for [T] {
|
10
|
2
|
fn pairwise(&self) -> Zip<Iter<T>, Skip<Iter<T>>> {
|
11
|
2
|
self.iter().zip(self.iter().skip(1))
|
12
|
|
}
|
13
|
|
}
|
14
|
|
|
15
|
2
|
fn _mean(s: &[f64]) -> f64 {
|
16
|
2
|
s.iter().map(|v| v / s.len() as f64).sum()
|
17
|
|
}
|
18
|
|
|
19
|
2
|
pub fn median(s: &[f64]) -> f64 {
|
20
|
2
|
let mut s = s.to_owned();
|
21
|
2
|
s.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
22
|
2
|
match s.len() % 2 {
|
23
|
2
|
0 => (s[(s.len() / 2) - 1] / 2.) + (s[(s.len() / 2)] / 2.),
|
24
|
2
|
_ => s[s.len() / 2],
|
25
|
|
}
|
26
|
|
}
|
27
|
|
|
28
|
2
|
pub fn quartiles(s: &[f64]) -> (f64, f64, f64) {
|
29
|
2
|
if s.len() == 1 {
|
30
|
2
|
return (s[0], s[0], s[0]);
|
31
|
|
}
|
32
|
2
|
let mut s = s.to_owned();
|
33
|
2
|
s.sort_by(|a, b| a.partial_cmp(b).unwrap());
|
34
|
2
|
let (a, b) = if s.len() % 2 == 0 {
|
35
|
2
|
s.split_at(s.len() / 2)
|
36
|
|
} else {
|
37
|
2
|
(&s[..(s.len() / 2)], &s[((s.len() / 2) + 1)..])
|
38
|
|
};
|
39
|
2
|
(median(a), median(&s), median(b))
|
40
|
|
}
|
41
|
|
|
42
|
|
/// Given a slice of numbers, return the minimum and maximum values
|
43
|
0
|
pub fn range(s: &[f64]) -> (f64, f64) {
|
44
|
0
|
let mut min = f64::INFINITY;
|
45
|
0
|
let mut max = f64::NEG_INFINITY;
|
46
|
0
|
for &v in s {
|
47
|
0
|
min = min.min(v);
|
48
|
0
|
max = max.max(v);
|
49
|
|
}
|
50
|
0
|
(min, max)
|
51
|
|
}
|
52
|
|
|
53
|
|
/// Floor or ceiling the min or max to zero to avoid them both having the same value
|
54
|
2
|
pub fn pad_range_to_zero(min: f64, max: f64) -> (f64, f64) {
|
55
|
2
|
if (min - max).abs() < std::f64::EPSILON {
|
56
|
|
(
|
57
|
2
|
if min > 0. { 0. } else { min },
|
58
|
2
|
if max < 0. { 0. } else { max },
|
59
|
|
)
|
60
|
|
} else {
|
61
|
2
|
(min, max)
|
62
|
|
}
|
63
|
|
}
|
64
|
|
|
65
|
|
#[cfg(test)]
|
66
|
|
mod tests {
|
67
|
|
use super::*;
|
68
|
|
|
69
|
|
#[test]
|
70
|
2
|
fn test_pairwise() {
|
71
|
2
|
let a = [1, 2, 3, 4, 5];
|
72
|
2
|
assert_eq!(a.pairwise().next().unwrap(), (&1, &2));
|
73
|
2
|
assert_eq!(a.pairwise().last().unwrap(), (&4, &5));
|
74
|
2
|
assert_eq!(a.pairwise().len(), a.len() - 1);
|
75
|
|
|
76
|
2
|
let a = [1, 2];
|
77
|
2
|
assert_eq!(a.pairwise().next().unwrap(), (&1, &2));
|
78
|
2
|
assert_eq!(a.pairwise().last().unwrap(), (&1, &2));
|
79
|
2
|
assert_eq!(a.pairwise().len(), a.len() - 1);
|
80
|
|
|
81
|
2
|
let a = [1];
|
82
|
2
|
assert!(a.pairwise().next().is_none());
|
83
|
|
|
84
|
2
|
let b: Vec<f64> = vec![0.0, 0.1, 0.2];
|
85
|
2
|
assert_eq!(b.pairwise().next().unwrap(), (&0.0, &0.1));
|
86
|
|
}
|
87
|
|
|
88
|
|
#[test]
|
89
|
2
|
fn test_mean() {
|
90
|
|
// TODO should error: mean(&[]);
|
91
|
2
|
assert_eq!(_mean(&[1.]), 1.);
|
92
|
2
|
assert_eq!(_mean(&[1., 2.]), 1.5);
|
93
|
2
|
assert_eq!(_mean(&[1., 2., 3.]), 2.);
|
94
|
|
}
|
95
|
|
|
96
|
|
#[test]
|
97
|
2
|
fn test_median() {
|
98
|
|
// TODO should error: median(&[]);
|
99
|
2
|
assert_eq!(median(&[1.]), 1.);
|
100
|
2
|
assert_eq!(median(&[1., 2.]), 1.5);
|
101
|
2
|
assert_eq!(median(&[1., 2., 4.]), 2.);
|
102
|
2
|
assert_eq!(median(&[1., 2., 3., 7.]), 2.5);
|
103
|
|
}
|
104
|
|
|
105
|
|
#[test]
|
106
|
2
|
fn test_quartiles() {
|
107
|
|
// TODO should error: quartiles(&[]);
|
108
|
2
|
assert_eq!(quartiles(&[1.]), (1., 1., 1.));
|
109
|
2
|
assert_eq!(quartiles(&[1., 2.]), (1., 1.5, 2.));
|
110
|
2
|
assert_eq!(quartiles(&[1., 2., 4.]), (1., 2., 4.));
|
111
|
2
|
assert_eq!(quartiles(&[1., 2., 3., 4.]), (1.5, 2.5, 3.5));
|
112
|
|
}
|
113
|
|
|
114
|
|
#[test]
|
115
|
2
|
fn test_pad_range_to_zero() {
|
116
|
2
|
assert_eq!(pad_range_to_zero(2.0, 2.0), (0.0, 2.0));
|
117
|
2
|
assert_eq!(pad_range_to_zero(-2.0, 2.0), (-2.0, 2.0));
|
118
|
2
|
assert_eq!(pad_range_to_zero(-2.0, -2.0), (-2.0, 0.0));
|
119
|
|
}
|
120
|
|
}
|