use crate::common::{StatError, TailType, TestResult, calculate_ci, calculate_p};
use statrs::distribution::StudentsT;
pub fn t_test<I, T>(
data: I,
pop_mean: f64,
tail: TailType,
alpha: f64,
) -> Result<TestResult, StatError>
where
I: IntoIterator<Item = T>,
T: Into<f64>,
{
let sample_data: Vec<f64> = data.into_iter().map(|x| x.into()).collect();
if sample_data.is_empty() {
return Err(StatError::EmptyData);
}
if sample_data.len() < 2 {
return Err(StatError::InsufficientData);
}
let n = sample_data.len() as f64;
let sample_mean = sample_data.iter().sum::<f64>() / n;
let sample_var = sample_data
.iter()
.map(|x| (x - sample_mean).powi(2))
.sum::<f64>()
/ (n - 1.0);
let std_error = (sample_var / n).sqrt();
let test_statistic = (sample_mean - pop_mean) / std_error;
let df = n - 1.0;
let t_dist = StudentsT::new(0.0, 1.0, df).map_err(|e| {
StatError::ComputeError(format!("Failed to create StudentsT distribution: {e}"))
})?;
let p_value = calculate_p(test_statistic, tail.clone(), &t_dist);
let confidence_interval = calculate_ci(sample_mean, std_error, alpha, &t_dist);
let reject_null = p_value < alpha;
let null_hypothesis = match tail {
TailType::Left => format!("H0: µ >= {pop_mean}"),
TailType::Right => format!("H0: µ <= {pop_mean}"),
TailType::Two => format!("H0: µ = {pop_mean}"),
};
let alt_hypothesis = match tail {
TailType::Left => format!("Ha: µ < {pop_mean}"),
TailType::Right => format!("Ha: µ > {pop_mean}"),
TailType::Two => format!("Ha: µ ≠ {pop_mean}"),
};
Ok(TestResult {
test_statistic,
p_value,
confidence_interval,
null_hypothesis,
alt_hypothesis,
reject_null,
})
}