#![warn(missing_docs)]
pub mod algos;
mod error;
use crate::algos::{naive_median, r_median};
use crate::error::MedError;
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) -> Result<f64,MedError<String>>;
fn mad(self, median: f64) -> Result<f64,MedError<String>>;
fn medstats(self) -> Result<MStats,MedError<String>>;
fn medinfo(self) -> Result<Med,MedError<String>>;
}
impl<T> Median for &[T]
where
T: Copy + PartialOrd,
f64: From<T>,
{
fn median(self) -> Result<f64,MedError<String>> {
let n = self.len();
if n == 0 {
return Err(MedError::SizeError("median: zero length data".to_owned()));
};
if n < 50 {
naive_median(self)
} else {
Ok(r_median(self))
}
}
fn mad(self, med: f64) -> Result<f64,MedError<String>> {
self.iter()
.map(|&s| ((f64::from(s) - med).abs()))
.collect::<Vec<f64>>()
.median()
}
fn medstats(self) -> Result<MStats,MedError<String>> {
let centre = self.median()?;
Ok(MStats {
centre,
dispersion: self.mad(centre)?,
})
}
fn medinfo(self) -> Result<Med,MedError<String>> {
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()?;
Ok(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()?;
Ok(Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
})
}
}
}