use single_utilities::traits::FloatOps;
use std::collections::HashMap;
pub mod correction;
pub mod effect;
pub mod inference;
pub mod utils;
#[derive(Debug, Clone, Copy)]
pub enum TestMethod {
TTest(TTestType),
MannWhitney,
FisherExact,
NegativeBinomial,
ZeroInflated,
}
#[derive(Debug, Clone, Copy)]
pub enum TTestType {
Student,
Welch,
}
#[derive(Debug, Clone, Copy)]
pub enum Alternative {
TwoSided,
Less,
Greater,
}
#[derive(Debug, Clone)]
pub struct TestResult<T> {
pub statistic: T,
pub p_value: T,
pub confidence_interval: Option<(T, T)>,
pub degrees_of_freedom: Option<T>,
pub effect_size: Option<T>,
pub standard_error: Option<T>,
pub metadata: HashMap<String, T>,
}
impl<T> TestResult<T>
where
T: FloatOps,
{
pub fn new(statistic: T, p_value: T) -> Self {
TestResult {
statistic,
p_value,
confidence_interval: None,
degrees_of_freedom: None,
effect_size: None,
standard_error: None,
metadata: HashMap::new(),
}
}
pub fn with_effect_size(statistic: T, p_value: T, effect_size: T) -> Self {
TestResult {
statistic,
p_value,
confidence_interval: None,
degrees_of_freedom: None,
effect_size: Some(effect_size),
standard_error: None,
metadata: HashMap::new(),
}
}
pub fn with_confidence_interval(mut self, lower: T, upper: T) -> Self {
self.confidence_interval = Some((lower, upper));
self
}
pub fn with_degrees_of_freedom(mut self, df: T) -> Self {
self.degrees_of_freedom = Some(df);
self
}
pub fn with_standard_error(mut self, se: T) -> Self {
self.standard_error = Some(se);
self
}
pub fn with_metadata(mut self, key: &str, value: T) -> Self {
self.metadata.insert(key.to_string(), value);
self
}
pub fn is_significant(&self, alpha: T) -> bool {
self.p_value < alpha
}
}
#[derive(Debug, Clone)]
pub struct MultipleTestResults<T> {
pub statistics: Vec<T>,
pub p_values: Vec<T>,
pub adjusted_p_values: Option<Vec<T>>,
pub effect_sizes: Option<Vec<T>>,
pub confidence_intervals: Option<Vec<(T, T)>>,
pub feature_metadata: Option<Vec<HashMap<String, T>>>,
pub global_metadata: HashMap<String, String>,
}
impl<T> MultipleTestResults<T>
where
T: FloatOps,
{
pub fn new(statistics: Vec<T>, p_values: Vec<T>) -> Self {
MultipleTestResults {
statistics,
p_values,
adjusted_p_values: None,
effect_sizes: None,
confidence_intervals: None,
feature_metadata: None,
global_metadata: HashMap::new(),
}
}
pub fn with_adjusted_p_values(mut self, adjusted_p_values: Vec<T>) -> Self {
self.adjusted_p_values = Some(adjusted_p_values);
self
}
pub fn with_effect_sizes(mut self, effect_sizes: Vec<T>) -> Self {
self.effect_sizes = Some(effect_sizes);
self
}
pub fn with_confidence_intervals(mut self, confidence_intervals: Vec<(T, T)>) -> Self {
self.confidence_intervals = Some(confidence_intervals);
self
}
pub fn with_global_metadata(mut self, key: &str, value: &str) -> Self {
self.global_metadata
.insert(key.to_string(), value.to_string());
self
}
pub fn significant_indices(&self, alpha: T) -> Vec<usize> {
match &self.adjusted_p_values {
Some(adj_p_values) => adj_p_values
.iter()
.enumerate()
.filter_map(|(i, &p)| if p < alpha { Some(i) } else { None })
.collect(),
None => self
.p_values
.iter()
.enumerate()
.filter_map(|(i, &p)| if p < alpha { Some(i) } else { None })
.collect(),
}
}
pub fn num_significant(&self, alpha: T) -> usize {
self.significant_indices(alpha).len()
}
pub fn top_features(&self, n: usize) -> Vec<usize> {
let p_values = match &self.adjusted_p_values {
Some(adj_p) => adj_p,
None => &self.p_values,
};
let mut indices: Vec<usize> = (0..p_values.len()).collect();
indices.sort_by(|&a, &b| {
p_values[a]
.partial_cmp(&p_values[b])
.unwrap_or(std::cmp::Ordering::Equal)
});
indices.truncate(n);
indices
}
}