• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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         unsafe {
58             let q1 = self.at_unchecked(A::cast(25));
59             let q3 = self.at_unchecked(A::cast(75));
60 
61             q3 - q1
62         }
63     }
64 
65     /// Returns the 50th percentile
median(&self) -> A66     pub fn median(&self) -> A {
67         unsafe { self.at_unchecked(A::cast(50)) }
68     }
69 
70     /// Returns the 25th, 50th and 75th percentiles
quartiles(&self) -> (A, A, A)71     pub fn quartiles(&self) -> (A, A, A) {
72         unsafe {
73             (
74                 self.at_unchecked(A::cast(25)),
75                 self.at_unchecked(A::cast(50)),
76                 self.at_unchecked(A::cast(75)),
77             )
78         }
79     }
80 }
81