-
Notifications
You must be signed in to change notification settings - Fork 317
Expand file tree
/
Copy pathquartiles.rs
More file actions
154 lines (144 loc) · 4.25 KB
/
quartiles.rs
File metadata and controls
154 lines (144 loc) · 4.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
/// The quartiles
#[derive(Clone, Debug)]
pub struct Quartiles {
lower_fence: f64,
lower: f64,
median: f64,
upper: f64,
upper_fence: f64,
}
impl Quartiles {
// Extract a value representing the `pct` percentile of a
// sorted `s`, using linear interpolation.
fn percentile_of_sorted<T: Into<f64> + Copy>(s: &[T], pct: f64) -> f64 {
assert!(!s.is_empty());
if s.len() == 1 {
return s[0].into();
}
assert!(0_f64 <= pct);
let hundred = 100_f64;
assert!(pct <= hundred);
if (pct - hundred).abs() < std::f64::EPSILON {
return s[s.len() - 1].into();
}
let length = (s.len() - 1) as f64;
let rank = (pct / hundred) * length;
let lower_rank = rank.floor();
let d = rank - lower_rank;
let n = lower_rank as usize;
let lo = s[n].into();
let hi = s[n + 1].into();
lo + (hi - lo) * d
}
/// Create a new quartiles struct with the values calculated from the argument.
///
/// - `s`: The array of the original values
/// - **returns** The newly created quartiles
///
/// ```rust
/// use plotters::prelude::*;
///
/// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
/// assert_eq!(quartiles.median(), 37.5);
/// ```
pub fn new<T: Into<f64> + Copy + PartialOrd>(s: &[T]) -> Self {
let mut s = s.to_owned();
s.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
let lower = Quartiles::percentile_of_sorted(&s, 25_f64);
let median = Quartiles::percentile_of_sorted(&s, 50_f64);
let upper = Quartiles::percentile_of_sorted(&s, 75_f64);
let iqr = upper - lower;
let lower_fence = lower - 1.5 * iqr;
let upper_fence = upper + 1.5 * iqr;
Self {
lower_fence,
lower,
median,
upper,
upper_fence,
}
}
/// Create a new quartiles struct with pre-calculated values.
///
/// - `s`: The array of the original values
/// - **returns** The newly created quartiles
///
/// ```rust
/// use plotters::prelude::*;
///
/// let quartiles = Quartiles::new_from_values(&[2, 24, 31, 39, 45]);
/// assert_eq!(quartiles.values(), [2.0, 24.0, 31.0, 39.0, 45.0]);
/// ```
pub fn new_from_values<T: Into<f64> + Copy + PartialOrd>(s: &[T; 5]) -> Self {
let s = s.to_owned();
assert!(s[0] <= s[1]);
assert!(s[1] <= s[2]);
assert!(s[2] <= s[3]);
assert!(s[3] <= s[4]);
Self {
lower_fence: s[0].into(),
lower: s[1].into(),
median: s[2].into(),
upper: s[3].into(),
upper_fence: s[4].into(),
}
}
/// Get the quartiles values.
///
/// - **returns** The array [lower fence, lower quartile, median, upper quartile, upper fence]
///
/// ```rust
/// use plotters::prelude::*;
///
/// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
/// let values = quartiles.values();
/// assert_eq!(values, [-9.0, 20.25, 37.5, 39.75, 69.0]);
/// ```
pub fn values(&self) -> [f32; 5] {
[
self.lower_fence as f32,
self.lower as f32,
self.median as f32,
self.upper as f32,
self.upper_fence as f32,
]
}
/// Get the quartiles median.
///
/// - **returns** The median
///
/// ```rust
/// use plotters::prelude::*;
///
/// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
/// assert_eq!(quartiles.median(), 37.5);
/// ```
pub fn median(&self) -> f64 {
self.median
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[should_panic]
fn test_empty_input() {
let empty_array: [i32; 0] = [];
Quartiles::new(&empty_array);
}
#[test]
fn test_low_inputs() {
assert_eq!(
Quartiles::new(&[15.0]).values(),
[15.0, 15.0, 15.0, 15.0, 15.0]
);
assert_eq!(
Quartiles::new(&[10, 20]).values(),
[5.0, 12.5, 15.0, 17.5, 25.0]
);
assert_eq!(
Quartiles::new(&[10, 20, 30]).values(),
[0.0, 15.0, 20.0, 25.0, 40.0]
);
}
}