#![warn(missing_docs)]
pub mod algos;
use crate::algos::{naive_median, r_median};
use indxvec::{
printing::{GR, UN},
Vecops,
};
#[derive(Default)]
pub struct Med {
pub median: f64,
pub lq: f64,
pub uq: f64,
pub mad: f64,
pub ste: f64,
}
impl std::fmt::Display for Med {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"median:
\tLower Q: {GR}{:>.16}{UN}
\tMedian: {GR}{:>.16}{UN}
\tUpper Q: {GR}{:>.16}{UN}
\tMad: {GR}{:>.16}{UN}
\tStd Err: {GR}{:>.16}{UN}",
self.lq, self.median, self.uq, self.mad, self.ste
)
}
}
#[derive(Default)]
pub struct MStats {
pub centre: f64,
pub dispersion: f64,
}
impl std::fmt::Display for MStats {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"centre: {GR}{:<10e}{UN}\tdispersion: {GR}{:<10e}{UN}",
self.centre, self.dispersion
)
}
}
pub trait Median {
fn median(self) -> f64;
fn mad(self, median: f64) -> f64;
fn medstats(self) -> MStats;
fn medinfo(self) -> Med;
}
impl<T> Median for &[T]
where
T: Copy + PartialOrd,
f64: From<T>,
{
fn median(self) -> f64 {
let n = self.len();
if n == 0 {
return 0_f64;
};
if n < 50 {
naive_median(self)
} else {
r_median(self)
}
}
fn mad(self, med: f64) -> f64 {
self.iter()
.map(|&s| ((f64::from(s) - med).abs()))
.collect::<Vec<f64>>()
.median()
}
fn medstats(self) -> MStats {
let centre = self.median();
MStats {
centre,
dispersion: self.mad(centre),
}
}
fn medinfo(self) -> Med {
let mut equals = 0_usize;
let mut posdifs: Vec<f64> = Vec::new();
let mut negdifs: Vec<f64> = Vec::new();
let med = self.median();
for &s in self {
let sf = f64::from(s);
if sf > med {
posdifs.push(sf - med)
} else if sf < med {
negdifs.push(med - sf)
} else {
equals += 1
};
}
if equals > 1 {
let eqhalf = vec![0.; equals / 2];
let eqslice = vec![0.; equals];
let lq = med - negdifs.unite_unsorted(&eqhalf).median();
let uq = med + eqhalf.unite_unsorted(&posdifs).median();
let mad = [negdifs, eqslice, posdifs].concat().median();
Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
}
} else {
let lq = med - negdifs.median();
let uq = med + posdifs.median();
let mad = [negdifs, posdifs].concat().median();
Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
}
}
}
}