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