ndarray_stats/quantile/
interpolate.rs1use noisy_float::types::N64;
3use num_traits::{Float, FromPrimitive, NumOps, ToPrimitive};
4
5fn float_quantile_index(q: N64, len: usize) -> N64 {
6 q * ((len - 1) as f64)
7}
8
9fn float_quantile_index_fraction(q: N64, len: usize) -> N64 {
14 float_quantile_index(q, len).fract()
15}
16
17pub(crate) fn lower_index(q: N64, len: usize) -> usize {
19 float_quantile_index(q, len).floor().to_usize().unwrap()
20}
21
22pub(crate) fn higher_index(q: N64, len: usize) -> usize {
24 float_quantile_index(q, len).ceil().to_usize().unwrap()
25}
26
27pub trait Interpolate<T> {
31 #[doc(hidden)]
34 fn needs_lower(q: N64, len: usize) -> bool;
35
36 #[doc(hidden)]
39 fn needs_higher(q: N64, len: usize) -> bool;
40
41 #[doc(hidden)]
46 fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T;
47
48 private_decl! {}
49}
50
51pub struct Higher;
53pub struct Lower;
55pub struct Nearest;
57pub struct Midpoint;
59pub struct Linear;
63
64impl<T> Interpolate<T> for Higher {
65 fn needs_lower(_q: N64, _len: usize) -> bool {
66 false
67 }
68 fn needs_higher(_q: N64, _len: usize) -> bool {
69 true
70 }
71 fn interpolate(_lower: Option<T>, higher: Option<T>, _q: N64, _len: usize) -> T {
72 higher.unwrap()
73 }
74 private_impl! {}
75}
76
77impl<T> Interpolate<T> for Lower {
78 fn needs_lower(_q: N64, _len: usize) -> bool {
79 true
80 }
81 fn needs_higher(_q: N64, _len: usize) -> bool {
82 false
83 }
84 fn interpolate(lower: Option<T>, _higher: Option<T>, _q: N64, _len: usize) -> T {
85 lower.unwrap()
86 }
87 private_impl! {}
88}
89
90impl<T> Interpolate<T> for Nearest {
91 fn needs_lower(q: N64, len: usize) -> bool {
92 float_quantile_index_fraction(q, len) < 0.5
93 }
94 fn needs_higher(q: N64, len: usize) -> bool {
95 !<Self as Interpolate<T>>::needs_lower(q, len)
96 }
97 fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T {
98 if <Self as Interpolate<T>>::needs_lower(q, len) {
99 lower.unwrap()
100 } else {
101 higher.unwrap()
102 }
103 }
104 private_impl! {}
105}
106
107impl<T> Interpolate<T> for Midpoint
108where
109 T: NumOps + Clone + FromPrimitive,
110{
111 fn needs_lower(_q: N64, _len: usize) -> bool {
112 true
113 }
114 fn needs_higher(_q: N64, _len: usize) -> bool {
115 true
116 }
117 fn interpolate(lower: Option<T>, higher: Option<T>, _q: N64, _len: usize) -> T {
118 let denom = T::from_u8(2).unwrap();
119 let lower = lower.unwrap();
120 let higher = higher.unwrap();
121 lower.clone() + (higher.clone() - lower.clone()) / denom.clone()
122 }
123 private_impl! {}
124}
125
126impl<T> Interpolate<T> for Linear
127where
128 T: NumOps + Clone + FromPrimitive + ToPrimitive,
129{
130 fn needs_lower(_q: N64, _len: usize) -> bool {
131 true
132 }
133 fn needs_higher(_q: N64, _len: usize) -> bool {
134 true
135 }
136 fn interpolate(lower: Option<T>, higher: Option<T>, q: N64, len: usize) -> T {
137 let fraction = float_quantile_index_fraction(q, len).to_f64().unwrap();
138 let lower = lower.unwrap();
139 let higher = higher.unwrap();
140 let lower_f64 = lower.to_f64().unwrap();
141 let higher_f64 = higher.to_f64().unwrap();
142 lower.clone() + T::from_f64(fraction * (higher_f64 - lower_f64)).unwrap()
143 }
144 private_impl! {}
145}