1 /// The quartiles
2 #[derive(Clone, Debug)]
3 pub struct Quartiles {
4     lower_fence: f64,
5     lower: f64,
6     median: f64,
7     upper: f64,
8     upper_fence: f64,
9 }
10 
11 impl Quartiles {
12     // Extract a value representing the `pct` percentile of a
13     // sorted `s`, using linear interpolation.
percentile_of_sorted<T: Into<f64> + Copy>(s: &[T], pct: f64) -> f6414     fn percentile_of_sorted<T: Into<f64> + Copy>(s: &[T], pct: f64) -> f64 {
15         assert!(!s.is_empty());
16         if s.len() == 1 {
17             return s[0].into();
18         }
19         assert!(0_f64 <= pct);
20         let hundred = 100_f64;
21         assert!(pct <= hundred);
22         if (pct - hundred).abs() < std::f64::EPSILON {
23             return s[s.len() - 1].into();
24         }
25         let length = (s.len() - 1) as f64;
26         let rank = (pct / hundred) * length;
27         let lower_rank = rank.floor();
28         let d = rank - lower_rank;
29         let n = lower_rank as usize;
30         let lo = s[n].into();
31         let hi = s[n + 1].into();
32         lo + (hi - lo) * d
33     }
34 
35     /// Create a new quartiles struct with the values calculated from the argument.
36     ///
37     /// - `s`: The array of the original values
38     /// - **returns** The newly created quartiles
39     ///
40     /// ```rust
41     /// use plotters::prelude::*;
42     ///
43     /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
44     /// assert_eq!(quartiles.median(), 37.5);
45     /// ```
new<T: Into<f64> + Copy + PartialOrd>(s: &[T]) -> Self46     pub fn new<T: Into<f64> + Copy + PartialOrd>(s: &[T]) -> Self {
47         let mut s = s.to_owned();
48         s.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
49 
50         let lower = Quartiles::percentile_of_sorted(&s, 25_f64);
51         let median = Quartiles::percentile_of_sorted(&s, 50_f64);
52         let upper = Quartiles::percentile_of_sorted(&s, 75_f64);
53         let iqr = upper - lower;
54         let lower_fence = lower - 1.5 * iqr;
55         let upper_fence = upper + 1.5 * iqr;
56         Self {
57             lower_fence,
58             lower,
59             median,
60             upper,
61             upper_fence,
62         }
63     }
64 
65     /// Get the quartiles values.
66     ///
67     /// - **returns** The array [lower fence, lower quartile, median, upper quartile, upper fence]
68     ///
69     /// ```rust
70     /// use plotters::prelude::*;
71     ///
72     /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
73     /// let values = quartiles.values();
74     /// assert_eq!(values, [-9.0, 20.25, 37.5, 39.75, 69.0]);
75     /// ```
values(&self) -> [f32; 5]76     pub fn values(&self) -> [f32; 5] {
77         [
78             self.lower_fence as f32,
79             self.lower as f32,
80             self.median as f32,
81             self.upper as f32,
82             self.upper_fence as f32,
83         ]
84     }
85 
86     /// Get the quartiles median.
87     ///
88     /// - **returns** The median
89     ///
90     /// ```rust
91     /// use plotters::prelude::*;
92     ///
93     /// let quartiles = Quartiles::new(&[7, 15, 36, 39, 40, 41]);
94     /// assert_eq!(quartiles.median(), 37.5);
95     /// ```
median(&self) -> f6496     pub fn median(&self) -> f64 {
97         self.median
98     }
99 }
100 
101 #[cfg(test)]
102 mod test {
103     use super::*;
104 
105     #[test]
106     #[should_panic]
test_empty_input()107     fn test_empty_input() {
108         let empty_array: [i32; 0] = [];
109         Quartiles::new(&empty_array);
110     }
111 
112     #[test]
test_low_inputs()113     fn test_low_inputs() {
114         assert_eq!(
115             Quartiles::new(&[15.0]).values(),
116             [15.0, 15.0, 15.0, 15.0, 15.0]
117         );
118         assert_eq!(
119             Quartiles::new(&[10, 20]).values(),
120             [5.0, 12.5, 15.0, 17.5, 25.0]
121         );
122         assert_eq!(
123             Quartiles::new(&[10, 20, 30]).values(),
124             [0.0, 15.0, 20.0, 25.0, 40.0]
125         );
126     }
127 }
128