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    ) -> 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                // Run t-tests
82                let results = self.t_test(
83                    &group1_indices,
84                    &group2_indices,
85                    test_type,
86                )?;
87
88                // Extract statistics and p-values
89                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                // Apply multiple testing correction
93                let adjusted_p_values =
94                    correction::benjamini_hochberg_correction(&p_values)?;
95
96                // Extract effect sizes if available
97                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                // Run Mann-Whitney tests
119                let results = self.mann_whitney_test(
120                    &group1_indices,
121                    &group2_indices,
122                    Alternative::TwoSided,
123                )?;
124
125                // Extract statistics and p-values
126                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                // Apply multiple testing correction
130                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            // Implement other test methods similarly
139            _ => Err(anyhow::anyhow!("Test method not implemented yet")),
140        }
141    }
142}