1#![allow(dead_code)]
6
7use crate::error::StatsResult;
8use crate::tests::ttest::Alternative;
9use scirs2_core::ndarray::{ArrayBase, Data, Ix1};
10use scirs2_core::numeric::Float;
11
12#[derive(Debug, Clone, Copy)]
14pub struct CorrelationResult<F> {
15 pub coefficient: F,
17 pub p_value: Option<F>,
19}
20
21impl<F: Float + std::fmt::Display> CorrelationResult<F> {
22 pub fn new(coefficient: F) -> Self {
24 Self {
25 coefficient,
26 p_value: None,
27 }
28 }
29
30 pub fn with_p_value(_coefficient: F, pvalue: F) -> Self {
32 Self {
33 coefficient: _coefficient,
34 p_value: Some(pvalue),
35 }
36 }
37}
38
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
41pub enum CorrelationMethod {
42 Pearson,
44 Spearman,
46 KendallTau,
48}
49
50impl CorrelationMethod {
51 pub fn from_str(s: &str) -> StatsResult<Self> {
53 match s.to_lowercase().as_str() {
54 "pearson" => Ok(CorrelationMethod::Pearson),
55 "spearman" => Ok(CorrelationMethod::Spearman),
56 "kendall" | "kendall_tau" | "kendalltau" => Ok(CorrelationMethod::KendallTau),
57 _ => Err(crate::error::StatsError::InvalidArgument(format!(
58 "Invalid correlation method: '{}'",
59 s
60 ))),
61 }
62 }
63}
64
65#[derive(Debug, Clone, Copy, PartialEq, Eq)]
67pub enum OptimizationHint {
68 Auto,
70 Scalar,
72 Simd,
74 Parallel,
76}
77
78#[derive(Debug, Clone)]
80pub struct StatsConfig {
81 pub optimization: OptimizationHint,
83 pub compute_p_value: bool,
85 pub alternative: Alternative,
87}
88
89impl Default for StatsConfig {
90 fn default() -> Self {
91 Self {
92 optimization: OptimizationHint::Auto,
93 compute_p_value: false,
94 alternative: Alternative::TwoSided,
95 }
96 }
97}
98
99impl StatsConfig {
100 pub fn with_p_value(mut self) -> Self {
102 self.compute_p_value = true;
103 self
104 }
105
106 pub fn with_alternative(mut self, alternative: Alternative) -> Self {
108 self.alternative = alternative;
109 self
110 }
111
112 pub fn with_optimization(mut self, optimization: OptimizationHint) -> Self {
114 self.optimization = optimization;
115 self
116 }
117}
118
119pub trait CorrelationExt<F, D>
121where
122 F: Float + std::fmt::Display + std::iter::Sum + Send + Sync,
123 D: Data<Elem = F>,
124{
125 fn correlation(
127 &self,
128 other: &ArrayBase<D, Ix1>,
129 method: CorrelationMethod,
130 config: Option<StatsConfig>,
131 ) -> StatsResult<CorrelationResult<F>>;
132
133 fn pearson(&self, other: &ArrayBase<D, Ix1>) -> StatsResult<F> {
135 self.correlation(other, CorrelationMethod::Pearson, None)
136 .map(|r| r.coefficient)
137 }
138
139 fn spearman(&self, other: &ArrayBase<D, Ix1>) -> StatsResult<F> {
141 self.correlation(other, CorrelationMethod::Spearman, None)
142 .map(|r| r.coefficient)
143 }
144
145 fn kendall(&self, other: &ArrayBase<D, Ix1>) -> StatsResult<F> {
147 self.correlation(other, CorrelationMethod::KendallTau, None)
148 .map(|r| r.coefficient)
149 }
150}
151
152pub struct StatsBuilder<F> {
154 data: Option<Vec<F>>,
155 config: StatsConfig,
156}
157
158impl<F: Float + std::fmt::Display + std::iter::Sum + Send + Sync> Default for StatsBuilder<F> {
159 fn default() -> Self {
160 Self::new()
161 }
162}
163
164impl<F: Float + std::fmt::Display + std::iter::Sum + Send + Sync> StatsBuilder<F> {
165 pub fn new() -> Self {
167 Self {
168 data: None,
169 config: StatsConfig::default(),
170 }
171 }
172
173 pub fn data(mut self, data: Vec<F>) -> StatsResult<Self> {
175 if data.is_empty() {
177 return Err(crate::error::StatsError::invalid_argument(
178 "Data cannot be empty",
179 ));
180 }
181
182 self.data = Some(data);
183 Ok(self)
184 }
185
186 pub fn data_unchecked(mut self, data: Vec<F>) -> Self {
188 self.data = Some(data);
189 self
190 }
191
192 pub fn with_p_value(mut self) -> Self {
194 self.config.compute_p_value = true;
195 self
196 }
197
198 pub fn alternative(mut self, alt: Alternative) -> Self {
200 self.config.alternative = alt;
201 self
202 }
203
204 pub fn optimization(mut self, opt: OptimizationHint) -> Self {
206 self.config.optimization = opt;
207 self
208 }
209
210 pub fn validate(&self) -> StatsResult<()> {
212 if self.data.is_none() {
213 return Err(crate::error::StatsError::invalid_argument(
214 "No data provided to builder",
215 ));
216 }
217
218 if let Some(ref data) = self.data {
219 if data.is_empty() {
220 return Err(crate::error::StatsError::invalid_argument(
221 "Data cannot be empty",
222 ));
223 }
224 }
225
226 Ok(())
227 }
228
229 pub fn getdata(&self) -> Option<&Vec<F>> {
231 self.data.as_ref()
232 }
233
234 pub fn get_config(&self) -> &StatsConfig {
236 &self.config
237 }
238}
239
240#[derive(Debug, Clone)]
242pub struct TestResult<F> {
243 pub statistic: F,
245 pub p_value: F,
247 pub df: Option<F>,
249 pub effectsize: Option<F>,
251 pub confidence_interval: Option<(F, F)>,
253}
254
255impl<F: Float + std::fmt::Display> TestResult<F> {
256 pub fn new(_statistic: F, pvalue: F) -> Self {
258 Self {
259 statistic: _statistic,
260 p_value: pvalue,
261 df: None,
262 effectsize: None,
263 confidence_interval: None,
264 }
265 }
266
267 pub fn with_df(mut self, df: F) -> Self {
269 self.df = Some(df);
270 self
271 }
272
273 pub fn with_effectsize(mut self, effectsize: F) -> Self {
275 self.effectsize = Some(effectsize);
276 self
277 }
278
279 pub fn with_confidence_interval(mut self, lower: F, upper: F) -> Self {
281 self.confidence_interval = Some((lower, upper));
282 self
283 }
284}
285
286#[cfg(test)]
287mod tests {
288 use super::*;
289
290 #[test]
291 fn test_correlation_method_from_str() {
292 assert_eq!(
293 CorrelationMethod::from_str("pearson").unwrap(),
294 CorrelationMethod::Pearson
295 );
296 assert_eq!(
297 CorrelationMethod::from_str("spearman").unwrap(),
298 CorrelationMethod::Spearman
299 );
300 assert_eq!(
301 CorrelationMethod::from_str("kendall").unwrap(),
302 CorrelationMethod::KendallTau
303 );
304 assert!(CorrelationMethod::from_str("invalid").is_err());
305 }
306
307 #[test]
308 fn test_stats_config_builder() {
309 let config = StatsConfig::default()
310 .with_p_value()
311 .with_alternative(Alternative::Greater);
312
313 assert!(config.compute_p_value);
314 assert_eq!(config.alternative, Alternative::Greater);
315 assert_eq!(config.optimization, OptimizationHint::Auto);
316 }
317}