use statrs::distribution::Discrete;
use statrs::distribution::Poisson;
use statrs::function::gamma::{gamma_li, gamma};
pub fn version()->String{
"1.0.3".to_string()
}
pub fn two_tailed_rates_equal(
num_events_one:f64,
t_one:f64,
num_events_two:f64,
t_two:f64)-> f64 {
let p_1 = one_tailed_ratio(num_events_one, t_one, num_events_two, t_two, 1.0);
let p_2 = one_tailed_ratio(num_events_one, t_one, num_events_two, t_two, 1.0);
return 2.0 * (p_1.min(p_2));
}
pub fn one_tailed_ratio(
num_events_one:f64,
t_one:f64,
num_events_two:f64,
t_two:f64,
h0_rate_ratio:f64) -> f64
{
assert!(num_events_one>0.0 || num_events_two>0.0,"We cannot test without some events occurring (parameter 1 and 3 were 0)");
let magic_d = t_two / t_one ;
let magic_g = h0_rate_ratio / magic_d ;
if cfg!(debug_assertions){
eprintln!("magic d: {} and g: {}", magic_d, magic_g);
}
let hypothesized_rate_one = (num_events_one + num_events_two) / (t_one * (1.0+1.0/magic_g));
let hypothesized_rate_two = (num_events_one + num_events_two)/ (t_two * (1.0+magic_g));
debug_assert!(hypothesized_rate_one>0.0);
let mut p_val = 0.0;
let obs_rate_one = num_events_one / t_one ;
let obs_rate_two = num_events_two / t_two ;
if cfg!(debug_assertions){
eprintln!("hyp rate 1 : {}, hyp rate 2 : {}",hypothesized_rate_one ,hypothesized_rate_two );
eprintln!("obs 1 : {}, obs 2 : {}",obs_rate_one,obs_rate_two);
}
if obs_rate_one == 0.0
{
p_val = Poisson::new(obs_rate_two * t_one ).unwrap().pmf(0);
} else if t_one>0.0 && t_two > 0.0{
let maximum_likelihood_h0:f64 =
Poisson::new(hypothesized_rate_one * t_one ).unwrap().pmf(num_events_one as u64)
* Poisson::new(hypothesized_rate_two * t_two ).unwrap().pmf(num_events_two as u64);
let maximum_likelihood_unconstrained:f64 =
Poisson::new(obs_rate_one * t_one ).unwrap().pmf(num_events_one as u64)
* Poisson::new(obs_rate_two * t_two ).unwrap().pmf(num_events_two as u64);
let lhr = maximum_likelihood_h0 / maximum_likelihood_unconstrained;
if lhr == 1.0{
p_val = 0.5;
} else {
let test_statistic:f64 = -2.0*(maximum_likelihood_h0 / maximum_likelihood_unconstrained).ln();
p_val = 0.5 * (1.0- gamma_li(0.5,test_statistic ) / gamma(0.5) );
}
}
p_val
}
#[cfg(test)]
mod tests{
use super::*;
use claim::{assert_lt,assert_gt};
#[test]
fn test_ones_side(){
let p = one_tailed_ratio(1.0,1.0,1.0,1.0,1.0);
assert_eq!(p,0.5);
}
#[test]
fn test_two_sides_null_hypothesis_true(){
let occurances_one = vec![1,1,1,1,1,1];
let occurances_two = vec![2,2,2,2,2,2];
let n1 = occurances_one.len() as f64;
let n2 = occurances_two.len() as f64;
let sum1 = occurances_one.iter().sum::<usize>() as f64;
let sum2 = occurances_two.iter().sum::<usize>() as f64;
let p = one_tailed_ratio(sum1, n1, sum2, n2, 0.5);
assert_eq!(p, 0.50);
let p = one_tailed_ratio(sum1, n1, sum2, n2, 0.49999 );
assert_gt!(p, 0.49);
let p_half = one_tailed_ratio(sum1, n1, sum2, n2, 0.49999);
let p_double = one_tailed_ratio(sum2, n2, sum1, n1, 2.0001);
assert_gt!(2.0*p_half.min(p_double),0.99);
}
#[test]
fn test_two_diff(){
let occurances_observed = vec![0,0,1,0];
let occurances_other = vec![1,1,5,3,3,8];
let n1 = occurances_observed.len() as f64;
let n2 = occurances_observed.len() as f64;
let sum1 = occurances_observed.iter().sum::<usize>() as f64;
let sum2 = occurances_other.iter().sum::<usize>() as f64;
let p = one_tailed_ratio(sum1, n1, sum2, n2, 1.0);
assert_lt!(p,0.01);
}
#[test]
fn test_two_same(){
let occurances_observed = vec![1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
let occurances_other = vec![1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1];
let n1 = occurances_observed.len() as f64;
let n2 = occurances_observed.len() as f64;
let sum1 = occurances_observed.iter().sum::<usize>() as f64;
let sum2 = occurances_other.iter().sum::<usize>() as f64;
let p_left = one_tailed_ratio(sum1, n1, sum2, n2, 1.0);
let p_right = one_tailed_ratio(sum2, n2, sum1, n1, 1.0);
assert_eq!(1.0,2.0*p_left.min(p_right));
}
#[test]
fn test_by_rate(){
let data = vec![0,1,1,0];
let n1 = data.len() as f64;
let sum1 = data.iter().sum::<usize>() as f64;
let expected_n = n1;
let expected_sum = 0.5 * n1;
let p = two_tailed_rates_equal(sum1, n1, expected_sum, expected_n);
assert!(p>0.99);
}
#[test]
fn test_readme_example(){
let occurances_one = vec![1,0,1,0,1,0];
let occurances_two = vec![1,1,1,1,0,2];
let n1 = occurances_one.len() as f64;
let n2 = occurances_two.len() as f64;
let sum1 = occurances_one.iter().sum::<usize>() as f64;
let sum2 = occurances_two.iter().sum::<usize>() as f64;
let p = one_tailed_ratio(sum1, n1, sum2, n2, 0.5);
assert_eq!(p, 0.50);
let p = one_tailed_ratio(sum1, n1, sum2, n2, 0.49999 );
assert_gt!(p, 0.49);
let p_half = one_tailed_ratio(sum1, n1, sum2, n2, 0.49999);
let p_double = one_tailed_ratio(sum2, n2, sum1, n1, 2.0001);
assert_gt!(2.0*p_half.min(p_double),0.99);
let mut p_double = two_tailed_rates_equal(sum2, n2, sum1, n1);
assert_lt!(p_double,0.25);
assert_gt!(p_double,0.15);
let trial2_one = vec![1,0,1,0,1,0,1,0,1,0,1,0,1,0];
let trial2_two = vec![1,1,1,1,0,2,0,2,1,1,0,2,1,1];
let t2n1 = trial2_one.len() as f64;
let t2n2 = trial2_two.len() as f64;
let t2sum1 = trial2_one.iter().sum::<usize>() as f64;
let t2sum2 = trial2_two.iter().sum::<usize>() as f64;
p_double = two_tailed_rates_equal(t2sum2, t2n2, t2sum1, t2n1);
assert_lt!(p_double,0.05);
}
}