use std::fmt;
use self::QType::*;
use crate::structure::matrix::*;
use crate::traits::matrix::{LinearAlgebra, MatrixTrait};
use order_stat::kth_by;
pub trait Statistics {
type Array;
type Value;
fn mean(&self) -> Self::Value;
fn var(&self) -> Self::Value;
fn sd(&self) -> Self::Value;
fn cov(&self) -> Self::Array;
fn cor(&self) -> Self::Array;
}
impl Statistics for Vec<f64> {
type Array = Vec<f64>;
type Value = f64;
fn mean(&self) -> f64 {
let mut xn = 0f64;
let mut n = 0f64;
for x in self.iter() {
n += 1f64;
xn += (x - xn) / n;
}
xn
}
fn var(&self) -> f64 {
let mut xn = 0f64;
let mut n = 0f64;
let mut m2n: f64 = 0f64;
for x in self.iter() {
n += 1f64;
let diff_1 = x - xn;
xn += diff_1 / n;
m2n += diff_1 * (x - xn);
}
assert_ne!(n, 1f64);
m2n / (n - 1f64)
}
fn sd(&self) -> f64 {
self.var().sqrt()
}
fn cov(&self) -> Vec<f64> {
unimplemented!()
}
fn cor(&self) -> Vec<f64> {
unimplemented!()
}
}
impl Statistics for Vec<f32> {
type Array = Vec<f32>;
type Value = f32;
fn mean(&self) -> f32 {
let mut xn = 0f32;
let mut n = 0f32;
for x in self.iter() {
n += 1f32;
xn += (x - xn) / n;
}
xn
}
fn var(&self) -> f32 {
let mut xn = 0f32;
let mut n = 0f32;
let mut m2n: f32 = 0f32;
for x in self.iter() {
n += 1f32;
let diff_1 = x - xn;
xn += diff_1 / n;
m2n += diff_1 * (x - xn);
}
assert_ne!(n, 1f32);
m2n / (n - 1f32)
}
fn sd(&self) -> f32 {
self.var().sqrt()
}
fn cov(&self) -> Vec<f32> {
unimplemented!()
}
fn cor(&self) -> Vec<f32> {
unimplemented!()
}
}
impl Statistics for Matrix {
type Array = Matrix;
type Value = Vec<f64>;
fn mean(&self) -> Vec<f64> {
let mut container: Vec<f64> = Vec::new();
let c = self.col;
for i in 0..c {
container.push(self.col(i).mean());
}
container
}
fn var(&self) -> Vec<f64> {
let mut container: Vec<f64> = Vec::new();
let c = self.col;
for i in 0..c {
container.push(self.col(i).var());
}
container
}
fn sd(&self) -> Vec<f64> {
let mut container: Vec<f64> = Vec::new();
let c = self.col;
for i in 0..c {
container.push(self.col(i).sd());
}
container
}
fn cov(&self) -> Self {
let c = self.col;
let mut m: Self = matrix(vec![0f64; c * c], c, c, self.shape);
for i in 0..c {
let m1 = self.col(i);
for j in 0..c {
let m2 = self.col(j);
m[(i, j)] = cov(&m1, &m2);
}
}
m
}
fn cor(&self) -> Self {
let c = self.col;
let mut m: Self = matrix(vec![0f64; c * c], c, c, self.shape);
for i in 0..c {
let m1 = self.col(i);
for j in 0..c {
let m2 = self.col(j);
m[(i, j)] = cor(&m1, &m2);
}
}
m
}
}
pub fn cov(v1: &[f64], v2: &[f64]) -> f64 {
let mut ss = 0f64;
let mut sx = 0f64;
let mut sy = 0f64;
let mut l = 0f64;
for (x, y) in v1.iter().zip(v2) {
ss += x * y;
sx += *x;
sy += *y;
l += 1f64;
}
assert_ne!(l, 1f64);
(ss / l - (sx * sy) / (l.powf(2f64))) * l / (l - 1f64)
}
pub fn cor(v1: &Vec<f64>, v2: &Vec<f64>) -> f64 {
cov(v1, v2) / (v1.sd() * v2.sd())
}
pub fn lm(input: &Matrix, target: &Matrix) -> Matrix {
let mut ones = vec![1f64; input.row * input.col];
ones.extend(&input.data);
let x = matrix(ones, input.row, input.col + 1, input.shape);
&x.pseudo_inv() * target
}
pub trait OrderedStat {
type Array;
type Value;
fn median(&self) -> Self::Value;
fn quantile(&self, q: f64, qtype: QType) -> Self::Value;
fn quantiles(&self, q: Vec<f64>, qtype: QType) -> Self::Array;
}
#[derive(Debug, Copy, Clone)]
pub enum QType {
Type1,
Type2,
Type3,
Type4,
Type5,
Type6,
Type7,
Type8,
Type9,
}
impl OrderedStat for Vec<f64> {
type Array = Self;
type Value = f64;
fn median(&self) -> Self::Value {
self.quantile(0.5, Type2)
}
fn quantile(&self, q: f64, qtype: QType) -> Self::Value {
let mut m = self.clone();
quantile_mut(&mut m, q, qtype)
}
fn quantiles(&self, q: Vec<f64>, qtype: QType) -> Self::Array {
let mut v = vec![0f64; q.len()];
let mut m = self.clone();
for i in 0..q.len() {
v[i] = quantile_mut(&mut m, q[i], qtype);
}
v
}
}
fn quantile_mut(v: &mut [f64], q: f64, t: QType) -> f64 {
let l = v.len();
let p = 1f64 / (l as f64);
let k = (q / p) as usize;
match t {
Type1 => {
let k = if q == 0f64 {
0
} else if q == 1f64 {
l - 1
} else if q - (k as f64) * p > 0f64 {
k
} else {
k - 1
};
*kth_by(v, k, |x, y| x.partial_cmp(y).unwrap())
}
Type2 => {
if q == 0f64 {
let k = 0;
*kth_by(v, k, |x, y| x.partial_cmp(y).unwrap())
} else if q == 1f64 {
let k = l - 1;
*kth_by(v, k, |x, y| x.partial_cmp(y).unwrap())
} else if q - (k as f64) * p > 0f64 {
*kth_by(v, k, |x, y| x.partial_cmp(y).unwrap())
} else {
let prev = *kth_by(v, k - 1, |x, y| x.partial_cmp(y).unwrap());
let next = *kth_by(v, k, |x, y| x.partial_cmp(y).unwrap());
(prev + next) / 2f64
}
}
_ => unimplemented!(),
}
}
pub fn quantile(v: &Vec<f64>, qtype: QType) -> Vec<f64> {
let q_vec = vec![0.0, 0.25, 0.5, 0.75, 1.0];
v.quantiles(q_vec, qtype)
}
#[allow(non_snake_case)]
#[derive(Debug, Clone, PartialEq)]
pub struct ConfusionMatrix {
pub TP: usize,
pub TN: usize,
pub FP: usize,
pub FN: usize,
}
impl ConfusionMatrix {
#[allow(non_snake_case)]
pub fn new<T: PartialEq + Clone + Copy>(y: &[T], y_hat: &[T], true_val: T) -> Self {
let mut TP = 0;
let mut TN = 0;
let mut FP = 0;
let mut FN = 0;
for (&y, &y_hat) in y.iter().zip(y_hat.iter()) {
if y == true_val && y_hat == true_val {
TP += 1;
} else if y != true_val && y_hat != true_val {
TN += 1;
} else if y != true_val && y_hat == true_val {
FP += 1;
} else if y == true_val && y_hat != true_val {
FN += 1;
}
}
Self { TP, TN, FP, FN }
}
#[allow(non_snake_case)]
pub fn P(&self) -> usize {
self.TP + self.FN
}
#[allow(non_snake_case)]
pub fn N(&self) -> usize {
self.TN + self.FP
}
#[allow(non_snake_case)]
pub fn TPR(&self) -> f64 {
self.TP as f64 / (self.TP + self.FN) as f64
}
#[allow(non_snake_case)]
pub fn TNR(&self) -> f64 {
self.TN as f64 / (self.TN + self.FP) as f64
}
#[allow(non_snake_case)]
pub fn PPV(&self) -> f64 {
self.TP as f64 / (self.TP + self.FP) as f64
}
#[allow(non_snake_case)]
pub fn NPV(&self) -> f64 {
self.TN as f64 / (self.TN + self.FN) as f64
}
#[allow(non_snake_case)]
pub fn FNR(&self) -> f64 {
self.FN as f64 / (self.FN + self.TP) as f64
}
#[allow(non_snake_case)]
pub fn FPR(&self) -> f64 {
self.FP as f64 / (self.FP + self.TN) as f64
}
#[allow(non_snake_case)]
pub fn FDR(&self) -> f64 {
self.FP as f64 / (self.FP + self.TP) as f64
}
#[allow(non_snake_case)]
pub fn FOR(&self) -> f64 {
self.FN as f64 / (self.FN + self.TN) as f64
}
#[allow(non_snake_case)]
pub fn LR_plus(&self) -> f64 {
self.TPR() / self.FPR()
}
#[allow(non_snake_case)]
pub fn LR_minus(&self) -> f64 {
self.FNR() / self.TNR()
}
#[allow(non_snake_case)]
pub fn PT(&self) -> f64 {
self.FPR().sqrt() / (self.TPR().sqrt() + self.FPR().sqrt())
}
#[allow(non_snake_case)]
pub fn TS(&self) -> f64 {
self.TP as f64 / (self.TP + self.FP + self.FN) as f64
}
#[allow(non_snake_case)]
pub fn prevalence(&self) -> f64 {
self.P() as f64 / (self.P() + self.N()) as f64
}
#[allow(non_snake_case)]
pub fn ACC(&self) -> f64 {
(self.TP + self.TN) as f64 / (self.P() + self.N()) as f64
}
#[allow(non_snake_case)]
pub fn BA(&self) -> f64 {
(self.TPR() + self.TNR()) / 2f64
}
#[allow(non_snake_case)]
pub fn F1(&self) -> f64 {
2f64 * self.TP as f64 / (2f64 * self.TP as f64 + self.FP as f64 + self.FN as f64)
}
#[allow(non_snake_case)]
pub fn MCC(&self) -> f64 {
let a = self.TP as f64;
let b = self.FP as f64;
let c = self.FN as f64;
let d = self.TN as f64;
(a * d - b * c) / ((a + b) * (a + c) * (d + b) * (d + c)).sqrt()
}
#[allow(non_snake_case)]
pub fn FM(&self) -> f64 {
(self.PPV() * self.TPR()).sqrt()
}
#[allow(non_snake_case)]
pub fn BM(&self) -> f64 {
self.TPR() + self.TNR() - 1f64
}
#[allow(non_snake_case)]
pub fn MK(&self) -> f64 {
self.PPV() + self.NPV() - 1f64
}
#[allow(non_snake_case)]
pub fn DOR(&self) -> f64 {
self.LR_plus() / self.LR_minus()
}
pub fn to_matrix(&self) -> Matrix {
let mut m = matrix(vec![0f64; 4], 2, 2, Row);
m[(0, 0)] = self.TP as f64;
m[(0, 1)] = self.FP as f64;
m[(1, 0)] = self.FN as f64;
m[(1, 1)] = self.TN as f64;
m
}
pub fn calc_metric(&self, metric: Metric) -> f64 {
match metric {
Metric::TPR => self.TPR(),
Metric::TNR => self.TNR(),
Metric::PPV => self.PPV(),
Metric::NPV => self.NPV(),
Metric::FNR => self.FNR(),
Metric::FPR => self.FPR(),
Metric::FDR => self.FDR(),
Metric::FOR => self.FOR(),
Metric::LR_plus => self.LR_plus(),
Metric::LR_minus => self.LR_minus(),
Metric::PT => self.PT(),
Metric::TS => self.TS(),
Metric::prevalence => self.prevalence(),
Metric::ACC => self.ACC(),
Metric::BA => self.BA(),
Metric::F1 => self.F1(),
Metric::MCC => self.MCC(),
Metric::FM => self.FM(),
Metric::BM => self.BM(),
Metric::MK => self.MK(),
Metric::DOR => self.DOR(),
}
}
pub fn calc_metrics(&self, metrics: &[Metric]) -> Vec<f64> {
metrics.iter().map(|m| self.calc_metric(*m)).collect()
}
pub fn summary(&self, metrics: &[Metric]) {
let width = metrics.iter().fold(0, |acc, m| {
if m.to_string().len() > acc {
m.to_string().len()
} else {
acc
}
});
println!("============================================================");
println!("Summary of metrics");
println!("============================================================");
for m in metrics {
println!("{:width$}:\t{:.4}", m.to_string(), self.calc_metric(*m));
}
println!("============================================================");
}
}
#[allow(non_camel_case_types)]
#[derive(Debug, Clone, Copy)]
pub enum Metric {
TPR,
TNR,
PPV,
NPV,
FNR,
FPR,
FDR,
FOR,
LR_plus,
LR_minus,
PT,
TS,
prevalence,
ACC,
BA,
F1,
MCC,
FM,
BM,
MK,
DOR,
}
impl fmt::Display for Metric {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Metric::TPR => write!(f, "TPR"),
Metric::TNR => write!(f, "TNR"),
Metric::PPV => write!(f, "PPV"),
Metric::NPV => write!(f, "NPV"),
Metric::FNR => write!(f, "FNR"),
Metric::FPR => write!(f, "FPR"),
Metric::FDR => write!(f, "FDR"),
Metric::FOR => write!(f, "FOR"),
Metric::LR_plus => write!(f, "LR+"),
Metric::LR_minus => write!(f, "LR-"),
Metric::PT => write!(f, "PT"),
Metric::TS => write!(f, "TS"),
Metric::prevalence => write!(f, "prevalence"),
Metric::ACC => write!(f, "ACC"),
Metric::BA => write!(f, "BA"),
Metric::F1 => write!(f, "F1"),
Metric::MCC => write!(f, "MCC"),
Metric::FM => write!(f, "FM"),
Metric::BM => write!(f, "BM"),
Metric::MK => write!(f, "MK"),
Metric::DOR => write!(f, "DOR"),
}
}
}