single_statistics/testing/inference/
mod.rs

1use 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                // Run t-tests
84                let results = self.t_test(
85                    &group1_indices,
86                    &group2_indices,
87                    test_type,
88                    Alternative::TwoSided,
89                )?;
90
91                // Extract statistics and p-values
92                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                // Apply multiple testing correction
96                let adjusted_p_values =
97                    correction::benjamini_hochberg_correction(&p_values)?;
98
99                // Extract effect sizes if available
100                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                // Run Mann-Whitney tests
122                let results = self.mann_whitney_test(
123                    &group1_indices,
124                    &group2_indices,
125                    Alternative::TwoSided,
126                )?;
127
128                // Extract statistics and p-values
129                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                // Apply multiple testing correction
133                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            // Implement other test methods similarly
142            _ => Err(anyhow::anyhow!("Test method not implemented yet")),
143        }
144    }
145}