use super::Statistic;
use crate::EmpiricalCDF;
#[derive(Debug, Clone, Copy)]
pub struct Quantile {
p: f64,
}
impl Quantile {
#[inline]
pub fn new(p: f64) -> Self {
debug_assert!((0.0..=1.0).contains(&p), "Quantile p must be in [0,1]");
Self { p }
}
#[inline]
pub fn median() -> Self {
Self { p: 0.5 }
}
}
impl<T: Clone> Statistic<EmpiricalCDF<T>, T> for Quantile {
#[inline]
fn compute(&self, ecdf: &EmpiricalCDF<T>) -> T {
let n = ecdf.n();
assert!(n > 0, "Quantile undefined for empty distribution");
let idx = ((n as f64 * self.p).ceil() as usize)
.saturating_sub(1)
.min(n - 1);
ecdf.points()[idx].clone()
}
}
#[derive(Debug, Clone, Copy)]
pub struct QuantileInterval {
lower: f64,
upper: f64,
}
impl QuantileInterval {
#[inline]
pub fn new(lower: f64, upper: f64) -> Self {
debug_assert!((0.0..=1.0).contains(&lower));
debug_assert!((0.0..=1.0).contains(&upper));
debug_assert!(lower <= upper);
Self { lower, upper }
}
#[inline]
pub fn percentile(confidence: f64) -> Self {
let alpha = 1.0 - confidence;
Self::new(alpha / 2.0, 1.0 - alpha / 2.0)
}
}
impl<T: Clone> Statistic<EmpiricalCDF<T>, (T, T)> for QuantileInterval {
#[inline]
fn compute(&self, ecdf: &EmpiricalCDF<T>) -> (T, T) {
let n = ecdf.n();
assert!(n > 0, "Quantile interval undefined for empty distribution");
let idx_low = ((n as f64 * self.lower).ceil() as usize)
.saturating_sub(1)
.min(n - 1);
let idx_up = ((n as f64 * self.upper).ceil() as usize)
.saturating_sub(1)
.min(n - 1);
let points = ecdf.points();
(points[idx_low].clone(), points[idx_up].clone())
}
}