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