1 use crate::stats::float::Float;
2 use cast::{self, usize};
3 
4 /// A "view" into the percentiles of a sample
5 pub struct Percentiles<A>(Box<[A]>)
6 where
7     A: Float;
8 
9 // TODO(rust-lang/rfcs#735) move this `impl` into a private percentiles module
10 impl<A> Percentiles<A>
11 where
12     A: Float,
13     usize: cast::From<A, Output = Result<usize, cast::Error>>,
14 {
15     /// Returns the percentile at `p`%
16     ///
17     /// Safety:
18     ///
19     /// - Make sure that `p` is in the range `[0, 100]`
at_unchecked(&self, p: A) -> A20     unsafe fn at_unchecked(&self, p: A) -> A {
21         let _100 = A::cast(100);
22         debug_assert!(p >= A::cast(0) && p <= _100);
23         debug_assert!(self.0.len() > 0);
24         let len = self.0.len() - 1;
25 
26         if p == _100 {
27             self.0[len]
28         } else {
29             let rank = (p / _100) * A::cast(len);
30             let integer = rank.floor();
31             let fraction = rank - integer;
32             let n = usize(integer).unwrap();
33             let &floor = self.0.get_unchecked(n);
34             let &ceiling = self.0.get_unchecked(n + 1);
35 
36             floor + (ceiling - floor) * fraction
37         }
38     }
39 
40     /// Returns the percentile at `p`%
41     ///
42     /// # Panics
43     ///
44     /// Panics if `p` is outside the closed `[0, 100]` range
at(&self, p: A) -> A45     pub fn at(&self, p: A) -> A {
46         let _0 = A::cast(0);
47         let _100 = A::cast(100);
48 
49         assert!(p >= _0 && p <= _100);
50         assert!(self.0.len() > 0);
51 
52         unsafe { self.at_unchecked(p) }
53     }
54 
55     /// Returns the interquartile range
iqr(&self) -> A56     pub fn iqr(&self) -> A {
57         let q1 = self.at(A::cast(25));
58         let q3 = self.at(A::cast(75));
59 
60         q3 - q1
61     }
62 
63     /// Returns the 50th percentile
median(&self) -> A64     pub fn median(&self) -> A {
65         self.at(A::cast(50))
66     }
67 
68     /// Returns the 25th, 50th and 75th percentiles
quartiles(&self) -> (A, A, A)69     pub fn quartiles(&self) -> (A, A, A) {
70         (
71             self.at(A::cast(25)),
72             self.at(A::cast(50)),
73             self.at(A::cast(75)),
74         )
75     }
76 }
77