#![warn(missing_docs)]
extern crate num_traits;
use num_traits::{Float, NumCast};
mod test;
fn trapezoidal<F: Float>(x: &[F], y: &[F]) -> F {
let mut prev_x = x[0];
let mut prev_y = y[0];
let mut integral = F::zero();
for (&x, &y) in x.iter().skip(1).zip(y.iter().skip(1)) {
integral = integral + (x - prev_x) * (prev_y + y) / NumCast::from(2.0).unwrap();
prev_x = x;
prev_y = y;
}
integral
}
fn check_data<F: Float>(v: &[(bool, F)]) -> bool {
v.iter().any(|x| x.0) && v.iter().any(|x| !x.0) && v.iter().all(|x| x.1.is_finite())
}
fn convert<T, X, F, I>(data: I, convert_fn: F) -> Vec<(bool, X)> where
I: IntoIterator<Item=T>,
F: Fn(T) -> (bool, X),
X: Float {
let data_it = data.into_iter();
let mut v = Vec::with_capacity(data_it.size_hint().0);
for i in data_it {
v.push(convert_fn(i));
}
v
}
pub fn roc_mut<F: Float>(pairs: &mut [(bool, F)]) -> Option<(Vec<F>, Vec<F>)> {
if !check_data(pairs) {
return None;
}
pairs.sort_unstable_by(&|x: &(_, F), y: &(_, F)|
match y.1.partial_cmp(&x.1) {
Some(ord) => ord,
None => unreachable!(),
});
let mut s0 = F::nan();
let (mut tp, mut fp) = (F::zero(), F::zero());
let (mut tps, mut fps) = (vec![], vec![]);
for &(t, s) in pairs.iter() {
if s != s0 {
tps.push(tp);
fps.push(fp);
s0 = s;
}
match t {
false => fp = fp + F::one(),
true => tp = tp + F::one(),
}
}
tps.push(tp);
fps.push(fp);
let (tp_max, fp_max) = (tps[tps.len() - 1], fps[fps.len() - 1]);
for tp in &mut tps {
*tp = *tp / tp_max;
}
for fp in &mut fps {
*fp = *fp / fp_max;
}
Some((fps, tps))
}
pub fn roc<T, X, F, I>(data: I, convert_fn: F) -> Option<(Vec<X>, Vec<X>)> where
I: IntoIterator<Item=T>,
F: Fn(T) -> (bool, X),
X: Float {
roc_mut(&mut convert(data, convert_fn))
}
pub fn pr<T, X, F, I>(data: I, convert_fn: F) -> Option<(Vec<X>, Vec<X>)> where
I: IntoIterator<Item=T>,
F: Fn(T) -> (bool, X),
X: Float {
pr_mut(&mut convert(data, convert_fn))
}
pub fn pr_mut<F: Float>(pairs: &mut [(bool, F)]) -> Option<(Vec<F>, Vec<F>)> {
if !check_data(pairs) {
return None;
}
pairs.sort_unstable_by(&|x: &(_, F), y: &(_, F)|
match y.1.partial_cmp(&x.1) {
Some(ord) => ord,
None => unreachable!(),
});
let mut x0 = F::nan();
let (mut tp, mut p, mut fp) = (F::zero(), F::zero(), F::zero());
let (mut recall, mut precision) = (vec![], vec![]);
let ln = pairs.iter().fold(F::zero(), |a,b| a + if b.0 { F::one() } else { F::zero() });
for &(l, x) in pairs.iter() {
if x != x0 {
recall.push(tp / ln);
precision.push(if p == F::zero() { F::one() } else { tp / (tp + fp) });
x0 = x;
}
p = p + F::one();
if l { tp = tp + F::one(); }
else { fp = fp + F::one(); }
}
recall.push(tp / ln);
precision.push(tp / p);
Some((precision, recall))
}
pub fn pr_auc<T, X, F, I>(data: I, convert_fn: F) -> Option<X> where
I: IntoIterator<Item=T>,
F: Fn(T) -> (bool, X),
X: Float {
pr_auc_mut(&mut convert(data, convert_fn))
}
pub fn pr_auc_mut<F: Float>(pairs: &mut [(bool, F)]) -> Option<F> {
pr_mut(pairs).map(|curve| {
trapezoidal(&curve.1, &curve.0)
})
}
pub fn roc_auc<T, X, F, I>(data: I, convert_fn: F) -> Option<X> where
I: IntoIterator<Item=T>,
F: Fn(T) -> (bool, X),
X: Float {
roc_auc_mut(&mut convert(data, convert_fn))
}
pub fn roc_auc_mut<F: Float>(pairs: &mut [(bool, F)]) -> Option<F> {
roc_mut(pairs).map(|curve| trapezoidal(&curve.0, &curve.1))
}