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