#![warn(missing_docs)]
pub mod algos;
pub mod error;
pub use crate::{algos::*, error::MedError};
use indxvec::{ printing::{GR, UN}, Vecops };
pub type ME = MedError<String>;
#[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 Medianf64 {
fn medianf64(self) -> Result<f64, ME>;
fn zeromedianf64(self) -> Result<Vec<f64>, ME>;
fn mediancorrf64(self, v: &mut[f64] ) -> Result<f64, MedError<String>>;
fn madf64(self, med: f64) -> Result<f64, ME>;
fn medstatsf64(self) -> Result<MStats, ME>;
fn medinfof64(self) -> Result<Med, ME>;
}
impl Medianf64 for &mut [f64] {
fn medianf64(self) -> Result<f64, ME> {
let n = self.len();
match n {
0 => Err(MedError::SizeError("median: zero length data".to_owned())),
1 => Ok(self[0]),
2 => Ok((self[0] + self[1]) / 2.0),
_ => Ok(autof64(self)),
}
}
fn zeromedianf64(self) -> Result<Vec<f64>, ME> {
let median = self.medianf64()?;
Ok(self.iter().map(|&s| s - median).collect())
}
fn mediancorrf64(self,v: &mut[f64]) -> Result<f64, ME> {
let mut sx2 = 0_f64;
let mut sy2 = 0_f64;
let selfmedian = self.medianf64()?;
let vmedian = v.medianf64()?;
let sxy: f64 = self
.iter()
.zip(v)
.map(|(&xt,yt)| {
let x = xt-selfmedian;
let y = *yt-vmedian;
sx2 += x * x;
sy2 += y * y;
x * y
})
.sum();
Ok(sxy / (sx2 * sy2).sqrt())
}
fn madf64(self, med: f64) -> Result<f64, ME> {
self.iter()
.map(|s| (s - med).abs())
.collect::<Vec<f64>>()
.medianf64()
}
fn medstatsf64(self) -> Result<MStats, ME> {
let centre = self.medianf64()?;
Ok(MStats {
centre,
dispersion: self.madf64(centre)?,
})
}
fn medinfof64(self) -> Result<Med, ME> {
let mut equals = 0_usize;
let mut posdifs: Vec<f64> = Vec::new();
let mut negdifs: Vec<f64> = Vec::new();
let med = self.medianf64()?;
for s in self {
let sf = *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).medianf64()?;
let uq = med + eqhalf.unite_unsorted(&posdifs).medianf64()?;
let mad = [negdifs, eqslice, posdifs].concat().medianf64()?;
Ok(Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
})
} else {
let lq = med - negdifs.medianf64()?;
let uq = med + posdifs.medianf64()?;
let mad = [negdifs, posdifs].concat().medianf64()?;
Ok(Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
})
}
}
}
pub trait Median<T> {
fn median(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<f64, ME>;
fn odd_strict_median(self) -> T
where
T: Ord + Clone;
fn even_strict_median(self) -> (T,T)
where
T: Ord + Clone;
fn zeromedian(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<Vec<f64>, ME>;
fn mediancorr(
self,
v: &[T],
quantify: &mut impl FnMut(&T) -> f64,
) -> Result<f64, MedError<String>>;
fn mad(self, med: f64, quantify: &mut impl FnMut(&T) -> f64) -> Result<f64, ME>;
fn medstats(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<MStats, ME>;
fn medinfo(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<Med, ME>;
}
impl<T> Median<T> for &[T] {
fn median(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<f64, ME> {
let n = self.len();
match n {
0 => Err(MedError::SizeError("median: zero length data".to_owned())),
1 => Ok(quantify(&self[0])),
2 => Ok((quantify(&self[0]) + quantify(&self[1])) / 2.0),
_ => Ok(auto_median(self, quantify)),
}
}
fn odd_strict_median(self) -> T
where
T: Ord + Clone,
{
self.max_1_min_k(self.len() / 2 + 1)
}
fn even_strict_median(self) -> (T,T)
where
T: Ord + Clone,
{
self.max_2_min_k(self.len() / 2 + 1)
}
fn zeromedian(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<Vec<f64>, ME> {
let median = self.median(quantify)?;
Ok(self.iter().map(|s| quantify(s) - median).collect())
}
fn mediancorr(
self,
v: &[T],
quantify: &mut impl FnMut(&T) -> f64,
) -> Result<f64, ME> {
let mut sx2 = 0_f64;
let mut sy2 = 0_f64;
let selfmedian = self.median(quantify)?;
let vmedian = v.median(quantify)?;
let sxy: f64 = self
.iter()
.zip(v)
.map(|(xt,yt)| {
let x = quantify(xt)-selfmedian;
let y = quantify(yt)-vmedian;
sx2 += x * x;
sy2 += y * y;
x * y
})
.sum();
Ok(sxy / (sx2 * sy2).sqrt())
}
fn mad(self, med: f64, quantify: &mut impl FnMut(&T) -> f64) -> Result<f64, ME> {
self.iter()
.map(|s| ((quantify(s) - med).abs()))
.collect::<Vec<f64>>()
.median(&mut |f: &f64| *f)
}
fn medstats(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<MStats, ME> {
let centre = self.median(quantify)?;
Ok(MStats {
centre,
dispersion: self.mad(centre, quantify)?,
})
}
fn medinfo(self, quantify: &mut impl FnMut(&T) -> f64) -> Result<Med, ME> {
let mut deref = |t: &f64| *t;
let mut equals = 0_usize;
let mut posdifs: Vec<f64> = Vec::new();
let mut negdifs: Vec<f64> = Vec::new();
let med = self.median(quantify)?;
for s in self {
let sf = quantify(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(&mut deref)?;
let uq = med + eqhalf.unite_unsorted(&posdifs).median(&mut deref)?;
let mad = [negdifs, eqslice, posdifs].concat().median(&mut deref)?;
Ok(Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
})
} else {
let lq = med - negdifs.median(&mut deref)?;
let uq = med + posdifs.median(&mut deref)?;
let mad = [negdifs, posdifs].concat().median(&mut deref)?;
Ok(Med {
median: med,
lq,
uq,
mad,
ste: mad / med,
})
}
}
}