use std::cmp::Ordering;
fn sum(l : &Vec<f64>) -> f64 {
l.iter().fold(0., |acc, val| acc+val)
}
fn mean(l : &Vec<f64>) -> f64 {
if l.len() == 0 {
panic!("Cannot calculate the mean of an empty Vec !");
}
sum(l)/(l.len() as f64)
}
fn constant(l : &Vec<f64>) -> bool {
if l.len() == 0 {
return true;
}
let k = l[0];
for i in 1..l.len() {
if k != l[i] {
return false;
}
}
true
}
fn cmp_f64(a: &f64, b: &f64) -> Ordering {
if a.is_nan() && b.is_nan() {
return Ordering::Equal;
}
if a.is_nan() {
return Ordering::Greater;
}
if b.is_nan() {
return Ordering::Less;
}
if a < b {
return Ordering::Less;
} else if a > b {
return Ordering::Greater;
}
return Ordering::Equal;
}
#[derive(Debug, PartialEq, Clone, Copy)]
enum Concordance {
Discordant,
Concordant,
Equal,
}
fn concordance(x1 : f64, y1 : f64, x2 : f64, y2 : f64) -> Concordance {
if x1==x2 && y1==y2 {
Concordance::Equal
}
else if (x1 < x2 && y1 < y2) || (x1 > x2 && y1 > y2) {
Concordance::Concordant
}
else {
Concordance::Discordant
}
}
fn rank(l : &Vec<f64>) -> Vec<f64> {
let n = l.len();
if n == 0 {
return Vec::new();
}
let mut l2 = (0..n).map(|i| (l[i], i)).collect::<Vec<(f64, usize)>>();
l2.sort_by(|a, b| cmp_f64(&a.0, &b.0));
let mut ranking = (0..n).map(|i| (l2[i].0, l2[i].1, i as f64)).collect::<Vec<(f64, usize, f64)>>();
let mut k = 1;
let mut h = 1;
let mut sum = ranking[0].2;
while k < ranking.len() {
if ranking[k-1].0 == ranking[k].0 {
h = h+1;
sum = sum + ranking[k].2;
}
else {
for i in (k-h)..k {
ranking[i].2 = sum / (h as f64);
}
h = 1;
sum = ranking[k].2;
}
k = k+1;
}
for i in (k-h)..k {
ranking[i].2 = sum / (h as f64);
}
ranking.sort_by_key(|x| x.1);
ranking.into_iter().map(|x| x.2).collect()
}
pub fn pearsonr(x : &Vec<f64>, y : &Vec<f64>) -> f64 {
assert!(x.len() == y.len());
let n = x.len();
let xm = mean(x);
let ym = mean(y);
let s1 = sum(&(0..n).map(|i| (x[i]-xm)*(y[i]-ym)).collect());
let s2 = sum(&(0..n).map(|i| (x[i]-xm).powf(2.)).collect());
let s3 = sum(&(0..n).map(|i| (y[i]-ym).powf(2.)).collect());
s1 / ( s2.sqrt() * s3.sqrt() )
}
pub fn spearmanr(x : &Vec<f64>, y : &Vec<f64>) -> f64 {
pearsonr(&rank(x), &rank(y))
}
pub fn kendalltau(x : &Vec<f64>, y : &Vec<f64>) -> f64 {
assert!(x.len() == y.len());
assert!(x.len() != 0);
if constant(x) || constant(y) {
return f64::NAN;
}
let n = x.len();
let mut discordant = 0;
let mut m = 0;
for i in 0..n {
for j in (i+1)..n {
if concordance(x[i],y[i],x[j],y[j]) == Concordance::Discordant {
discordant +=1;
}
m+=1;
}
}
1. - ((2 * discordant) as f64 / (m as f64))
}
#[cfg(test)]
mod tests_sum {
use super::*;
#[test]
fn sum_empty() {
let result = sum(&vec![]);
assert_eq!(result, 0.);
}
#[test]
fn sum_float() {
let result = sum(&vec![2.3, 1.6, 2.9, -4.8]);
assert_eq!(result, 2.0);
}
#[test]
#[should_panic]
fn mean_empty() {
let result = mean(&vec![]);
assert_eq!(result, 0.);
}
#[test]
fn mean_float() {
let result = mean(&vec![2.3, 1.6, 2.9, -4.8]);
assert_eq!(result, 0.5);
}
}
#[cfg(test)]
mod tests_float{
use super::*;
#[test]
fn greater() {
let result = cmp_f64(&0.2, &-0.1);
assert_eq!(result, Ordering::Greater);
}
#[test]
fn lesser() {
let result = cmp_f64(&0.1, &0.11);
assert_eq!(result, Ordering::Less);
}
#[test]
fn equal() {
let result = cmp_f64(&(1./3.), &(1./3.));
assert_eq!(result, Ordering::Equal);
}
#[test]
fn nan1() {
let result = cmp_f64(&f64::NAN, &(1./3.));
assert_eq!(result, Ordering::Greater);
}
#[test]
fn nan2() {
let result = cmp_f64(&(1./3.), &f64::NAN);
assert_eq!(result, Ordering::Less);
}
#[test]
fn nan3() {
let result = cmp_f64(&f64::NAN, &f64::NAN);
assert_eq!(result, Ordering::Equal);
}
#[test]
fn precision() {
let result = cmp_f64(&(2.3-1.1), &(1.2));
assert!(result != Ordering::Equal);
}
}
#[cfg(test)]
mod tests_constant {
#[test]
fn simple() {
let result = super::constant(&vec![3.4,5.1,2.6,7.3]);
assert_eq!(result, false);
}
#[test]
fn empty() {
let result = super::constant(&vec![]);
assert_eq!(result, true);
}
#[test]
fn double() {
let result = super::constant(&vec![2.3, 1.2, 1.2, 3.2]);
assert_eq!(result, false);
}
#[test]
fn constant() {
let result = super::constant(&vec![1., 1., 1., 1.]);
assert_eq!(result, true);
}
}
#[cfg(test)]
mod tests_rank {
use super::*;
#[test]
fn simple() {
let result = rank(&vec![3.4,5.1,2.6,7.3]);
assert_eq!(result, vec![1.,2.,0.,3.]);
}
#[test]
fn empty() {
let result = rank(&vec![]);
assert_eq!(result, vec![]);
}
#[test]
fn double() {
let result = rank(&vec![2.3, 1.2, 1.2, 3.2]);
assert_eq!(result, vec![2.0,0.5,0.5,3.0]);
}
#[test]
fn constant() {
let result = rank(&vec![1., 1., 1., 1.]);
assert_eq!(result, vec![1.5, 1.5, 1.5, 1.5]);
}
}