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