kernel-density-estimation 0.2.0

Kernel density estimation in Rust.
Documentation
use crate::float::{float, primitive, KDEFloat};

pub trait Sealed {}

pub(crate) fn variance<F: KDEFloat>(data: &[F]) -> F {
    let n = float!(data.len());
    let mean = data.iter().copied().sum::<F>() / n;
    let squares_sum = data.iter().map(|&x| (x - mean).powi(2)).sum::<F>();
    squares_sum / (n - F::one())
}

pub(crate) fn quantile<F: KDEFloat>(data: &[F], tau: F) -> F {
    assert!((0.0..=1.0).contains(&primitive!(tau)));
    let mut data = data.to_owned();
    data.sort_by(|a, b| a.partial_cmp(b).unwrap());
    let n = float!(data.len());
    let index = F::round(tau * (n + F::one())).to_usize().unwrap();
    data[index - 1]
}

pub(crate) fn interquartile_range<F: KDEFloat>(data: &[F]) -> F {
    let upper = float!(0.75);
    let lower = float!(0.25);
    quantile(data, upper) - quantile(data, lower)
}

pub(crate) fn cumsum<F: KDEFloat>(data: &[F]) -> Vec<F> {
    (0..data.len())
        .map(|i| data[..i + 1].iter().copied().sum())
        .collect()
}

#[cfg(test)]
mod tests {
    use approx::*;

    use super::{cumsum, interquartile_range, quantile, variance};

    #[test]
    fn test_variance() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        let res = variance(&data);
        assert_relative_eq!(res, 2.5);
    }

    #[test]
    fn test_quantile() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        let res = quantile(&data, 0.5);
        assert_relative_eq!(res, 3.0);
    }

    #[test]
    fn test_interquartile_range() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        let res = interquartile_range(&data);
        assert_relative_eq!(res, 3.0);
    }

    #[test]
    fn test_cumsum() {
        let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
        let res = cumsum(&data);
        let target = vec![1.0, 3.0, 6.0, 10.0, 15.0];
        assert_eq!(res, target);
    }
}