single_statistics/testing/inference/
mod.rs1use nalgebra_sparse::CsrMatrix;
2use single_utilities::traits::FloatOpsTS;
3use crate::testing::{correction, Alternative, MultipleTestResults, TTestType, TestMethod, TestResult};
4use crate::testing::utils::{extract_unique_groups, get_group_indices};
5
6pub mod discrete;
7
8pub mod parametric;
9
10pub mod nonparametric;
11
12pub trait MatrixStatTests<T>
13where
14 T: FloatOpsTS,
15{
16 fn t_test(
17 &self,
18 group1_indices: &[usize],
19 group2_indices: &[usize],
20 test_type: TTestType,
21 ) -> anyhow::Result<Vec<TestResult<f64>>>;
22
23
24 fn mann_whitney_test(
25 &self,
26 group1_indices: &[usize],
27 group2_indices: &[usize],
28 alternative: Alternative,
29 ) -> anyhow::Result<Vec<TestResult<f64>>>;
30
31 fn differential_expression(
32 &self,
33 group_ids: &[usize],
34 test_method: TestMethod,
35 ) -> anyhow::Result<MultipleTestResults<f64>>;
36}
37
38impl<T> MatrixStatTests<T> for CsrMatrix<T>
39where
40 T: FloatOpsTS
41{
42 fn t_test(
43 &self,
44 group1_indices: &[usize],
45 group2_indices: &[usize],
46 test_type: TTestType,
47 ) -> anyhow::Result<Vec<TestResult<f64>>> {
48 parametric::t_test_matrix_groups(
49 self,
50 group1_indices,
51 group2_indices,
52 test_type
53 )
54 }
55
56 fn mann_whitney_test(
57 &self,
58 group1_indices: &[usize],
59 group2_indices: &[usize],
60 alternative: Alternative,
61 ) -> anyhow::Result<Vec<TestResult<f64>>> {
62 nonparametric::mann_whitney_matrix_groups(self, group1_indices, group2_indices, alternative)
63 }
64
65 fn differential_expression(
66 &self,
67 group_ids: &[usize],
68 test_method: TestMethod,
69 ) -> anyhow::Result<MultipleTestResults<f64>> {
70 let unique_groups = extract_unique_groups(group_ids);
71 if unique_groups.len() != 2 {
72 return Err(anyhow::anyhow!(
73 "Currently only two-group comparisons are supported"
74 ));
75 }
76
77 let (group1_indices, group2_indices) = get_group_indices(group_ids, &unique_groups);
78
79 match test_method {
80 TestMethod::TTest(test_type) => {
81 let results = self.t_test(
83 &group1_indices,
84 &group2_indices,
85 test_type,
86 )?;
87
88 let statistics: Vec<_> = results.iter().map(|r| r.statistic).collect();
90 let p_values: Vec<_> = results.iter().map(|r| r.p_value).collect();
91
92 let adjusted_p_values =
94 correction::benjamini_hochberg_correction(&p_values)?;
95
96 let effect_sizes = results
98 .iter()
99 .map(|r| r.effect_size)
100 .collect::<Option<Vec<_>>>()
101 .unwrap_or_else(|| vec![0.0; results.len()])
102 .into_iter()
103 .filter_map(|x| Option::from(x))
104 .collect::<Vec<_>>();
105
106 let mut result = MultipleTestResults::new(statistics, p_values)
107 .with_adjusted_p_values(adjusted_p_values)
108 .with_global_metadata("test_type", "t_test");
109
110 if !effect_sizes.is_empty() {
111 result = result.with_effect_sizes(effect_sizes);
112 }
113
114 Ok(result)
115 }
116
117 TestMethod::MannWhitney => {
118 let results = self.mann_whitney_test(
120 &group1_indices,
121 &group2_indices,
122 Alternative::TwoSided,
123 )?;
124
125 let statistics: Vec<_> = results.iter().map(|r| r.statistic).collect();
127 let p_values: Vec<_> = results.iter().map(|r| r.p_value).collect();
128
129 let adjusted_p_values =
131 correction::benjamini_hochberg_correction(&p_values)?;
132
133 Ok(MultipleTestResults::new(statistics, p_values)
134 .with_adjusted_p_values(adjusted_p_values)
135 .with_global_metadata("test_type", "mann_whitney"))
136 }
137
138 _ => Err(anyhow::anyhow!("Test method not implemented yet")),
140 }
141 }
142}