Skip to main content

r2rs_stats/tests/
significance_of_regression.rs

1use 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}