r2rs_stats/tests/
significance_of_regression.rs1use std::fmt::Debug;
2
3use nalgebra::DMatrix;
4use num_traits::Float;
5use r2rs_nmath::{distribution::FBuilder, traits::Distribution};
6use strafe_trait::{
7 Assumption, Concept, Conclusion, NoSignificantFeature, RejectionStatus, SomeSignficantFeature,
8 Statistic, StatisticalTest,
9};
10use strafe_type::{Alpha64, FloatConstraint, ModelMatrix, Rational64};
11
12use crate::funcs::{regression_sum_of_squares, residual_sum_of_squares};
13
14#[derive(Copy, Clone, Debug)]
15pub struct SignificanceOfRegressionTest {
16 alpha: Alpha64,
17 scale: Option<f64>,
18 df: Option<Rational64>,
19}
20
21impl StatisticalTest for SignificanceOfRegressionTest {
22 type Input = (ModelMatrix, DMatrix<f64>, DMatrix<f64>, DMatrix<f64>);
23 type Output = Result<SignificanceOfRegressionStatistic, Box<dyn std::error::Error>>;
24
25 fn assumptions() -> Vec<Box<dyn Assumption>> {
26 Vec::new()
27 }
28
29 fn null_hypotheses() -> Vec<Box<dyn Conclusion>> {
30 vec![Box::new(NoSignificantFeature {})]
31 }
32
33 fn alternate_hypotheses() -> Vec<Box<dyn Conclusion>> {
34 vec![Box::new(SomeSignficantFeature {})]
35 }
36
37 fn test(&mut self, (x, y, b, w): &Self::Input) -> Self::Output {
38 let x = x.matrix();
39 let mut weighted_x = x.clone();
40 weighted_x
41 .column_iter_mut()
42 .for_each(|mut row| row.component_mul_assign(w));
43 let weighted_y = y.component_mul(w);
44
45 let n = x.shape().0;
46 let k = x.shape().1 - 1;
47
48 let regression_mean_square =
49 regression_sum_of_squares(&weighted_x, &weighted_y, b) / k as f64;
50
51 let residual_mean_square =
52 residual_sum_of_squares(&weighted_x, &weighted_y, b) / (n - k - 1) as f64;
53
54 let f = if residual_mean_square == 0.0 {
55 f64::infinity()
56 } else {
57 regression_mean_square / residual_mean_square
58 };
59
60 let mut f_distr_builder = FBuilder::new();
61 f_distr_builder.with_df1(k);
62 f_distr_builder.with_df2(n - k - 1);
63 let f_distr = f_distr_builder.build();
64 let p = f_distr.probability(f, false).unwrap();
65
66 Ok(SignificanceOfRegressionStatistic {
67 f,
68 p,
69 alpha: self.alpha,
70 })
71 }
72}
73
74#[derive(Copy, Clone, Debug)]
75pub struct SignificanceOfRegressionStatistic {
76 alpha: Alpha64,
77 f: f64,
78 p: f64,
79}
80
81impl Statistic for SignificanceOfRegressionStatistic {
82 fn alpha(&self) -> Alpha64 {
83 self.alpha
84 }
85
86 fn statistic(&self) -> f64 {
87 self.f
88 }
89
90 fn probability_value(&self) -> f64 {
91 self.p
92 }
93
94 fn conclusion(&self) -> RejectionStatus {
95 if self.p < self.alpha.unwrap() {
96 RejectionStatus::RejectInFavorOf(Box::new(SomeSignficantFeature::new()))
97 } else {
98 RejectionStatus::FailToReject(Box::new(NoSignificantFeature::new()))
99 }
100 }
101}
102
103impl Default for SignificanceOfRegressionTest {
104 fn default() -> Self {
105 Self {
106 alpha: 0.05.into(),
107 scale: None,
108 df: None,
109 }
110 }
111}
112
113impl SignificanceOfRegressionTest {
114 pub fn new() -> Self {
115 Self::default()
116 }
117
118 pub fn with_alpha<A: Into<Alpha64>>(self, alpha: A) -> Self {
119 Self {
120 alpha: alpha.into(),
121 ..self
122 }
123 }
124
125 pub fn with_scale(self, scale: f64) -> Self {
126 Self {
127 scale: Some(scale),
128 ..self
129 }
130 }
131
132 pub fn with_df<R: Into<Rational64>>(self, df: R) -> Self {
133 Self {
134 df: Some(df.into()),
135 ..self
136 }
137 }
138}