use statrs::distribution::StudentsT;
use statrs::distribution::ContinuousCDF;
use crate::utils::{Tails, Conclusion, mean, variance};
#[derive(Debug)]
#[derive(PartialEq)]
pub struct TResult {
pub test_type: &'static str,
pub statistic: f64,
pub df: f64,
pub p: f64,
}
pub fn test<Number: Into<f64> + Copy, Number2: Into<f64> + Copy>(data: Vec<Number>, expected_mean: Number2, tail: Tails, print_output: bool) -> Option<TResult> {
if data.len() == 0 { return None };
let observed_mean = mean(&data).unwrap();
let sample_size = data.len() as u32;
let sd = variance(&data).unwrap().sqrt();
test_dataless(observed_mean, expected_mean.into(), sample_size, sd, tail, print_output)
}
pub fn test_dataless<Number: Into<f64> + Copy>(observed_mean: Number, expected_mean: Number, sample_size: u32, pop_sd: Number, tail: Tails, print_output: bool) -> Option<TResult> {
if sample_size == 0 { panic!("\n[HYTE-Panic] Sample size must be greater than 0!\n") };
if pop_sd.into() < 0.0 { panic!("\n[HYTE-Panic] Standard deviation must not be a negative number!\n") };
let statistic: f64 = (observed_mean.into() - expected_mean.into()) / (pop_sd.into() / (sample_size as f64).sqrt());
let df = (sample_size - 1) as f64;
let t_distribution = StudentsT::new(0.0, 1.0, df).unwrap();
let p: f64;
let test_type: &'static str;
match tail {
Tails::LOWER => {
p = t_distribution.cdf(statistic);
test_type = "(1-Sample) One-Sided T-Test for Mean (Lower-Tailed)";
},
Tails::UPPER => {
p = 1.0 - t_distribution.cdf(statistic);
test_type = "(1-Sample) One-Sided T-Test for Mean (Upper-Tailed)";
},
Tails::BOTH => {
p = 2.0 * t_distribution.cdf(-statistic.abs());
test_type = "(1-Sample) Two-Sided T-Test for Mean"
},
}
let results = TResult {
test_type,
statistic,
df,
p,
};
if print_output {
match tail {
Tails::LOWER | Tails::UPPER => println!("\n----------------------- HYTE -----------------------\n\n{}\n\nZ test statistic = {:.2}\np-value = {:.3e}\nDegrees of freedom = {}\n\n----------------------- HYTE -----------------------\n", results.test_type, results.statistic, results.p, results.df),
Tails::BOTH => println!("\n--------------- HYTE ---------------\n\n{}\n\nZ test statistic = {:.2}\np-value = {:.3e}\nDegrees of freedom = {}\n\n--------------- HYTE ---------------\n", results.test_type, results.statistic, results.p, results.df),
}
}
Some(results)
}
pub fn test_two_samples<Number: Into<f64> + Copy>(data1: Vec<Number>, data2: Vec<Number>, print_output: bool) -> Option<TResult> {
if data1.len() == 0 {
return None
} else if data2.len() == 0 {
return None
};
let data1_mean = mean(&data1).unwrap();
let data2_mean = mean(&data2).unwrap();
let data1_variance = variance(&data1).unwrap();
let data2_variance = variance(&data2).unwrap();
let n1 = data1.len() as f64;
let n2 = data2.len() as f64;
let numerator = ((data1_variance / n1) + (data2_variance / n2)).powi(2);
let denominator = (data1_variance.powi(2) / (n1 * n1 * (n1 - 1.0))) + (data2_variance.powi(2) / (n2 * n2 * (n2 - 1.0)));
let df = numerator / denominator;
let statistic = (data1_mean - data2_mean) / ((data1_variance / n1) + (data2_variance / n2)).sqrt();
let t_distribution = StudentsT::new(0.0, 1.0, df as f64).unwrap();
let p = 2.0 * t_distribution.cdf(-statistic.abs());
let results = TResult {
test_type: "(2-Sample) T-Test for Mean",
statistic,
df,
p,
};
if print_output {println!("\n---------- HYTE ----------\n\n{}\n\nZ test statistic = {:.2}\np-value = {:.3e}\nDegrees of freedom = {:.2}\n\n---------- HYTE ----------\n", results.test_type, results.statistic, results.p, results.df)};
Some(results)
}
impl TResult {
pub fn conclude(&self, significance_level: f64, print_output: bool) -> Conclusion {
if self.p < significance_level {
if print_output {println!("\n---------------------------- HYTE ----------------------------\n\nStatistical Conclusion\n\np-value = ({:.3e})\nsignificance level = ({:.3e})\n\np-value < s. l, therefore reject H_0\n\nThere is sufficient evidence to reject the null hypothesis.\n\n---------------------------- HYTE ----------------------------\n", self.p, significance_level);};
Conclusion::Reject
} else {
if print_output {println!("\n---------------------------- HYTE ----------------------------\n\nStatistical Conclusion\n\np-value = ({:.3e})\nsignificance level = ({:.3e})\n\np-value > s. l, therefore do not reject H_0\n\nThere is insufficient evidence to reject the null hypothesis.\n\n---------------------------- HYTE ----------------------------\n", self.p, significance_level);};
Conclusion::DoNotReject
}
}
pub fn conclude_by_convention(&self, print_output: bool) -> Conclusion {
if self.p < 0.05 {
if print_output {println!("\n---------------------------- HYTE ----------------------------\n\nStatistical Conclusion\n\np-value = ({:.3e})\nsignificance level = 0.5\n\np-value < s. l, therefore reject H_0\n\nThere is sufficient evidence to reject the null hypothesis.\n\n---------------------------- HYTE ----------------------------\n", self.p);};
Conclusion::Reject
} else {
if print_output {println!("\n---------------------------- HYTE ----------------------------\n\nStatistical Conclusion\n\np-value = ({:.3e})\nsignificance level = 0.5\n\np-value > s. l, therefore do not reject H_0\n\nThere is insufficient evidence to reject the null hypothesis.\n\n---------------------------- HYTE ----------------------------\n", self.p);};
Conclusion::DoNotReject
}
}
}