r2rs_stats/tests/
residuals.rs1use nalgebra::DMatrix;
2use strafe_trait::{
3 Assumption, Concept, Conclusion, Normal, NormalResiduals, NotNormalResiduals, RejectionStatus,
4 Statistic, StatisticalTest,
5};
6use strafe_type::{Alpha64, ModelMatrix};
7
8use crate::{
9 funcs::residuals,
10 tests::swilk::{ShapiroWilkStatistic, ShapiroWilkTest},
11};
12
13#[derive(Copy, Clone, Debug)]
14pub struct NormalResidualTest {
15 alpha: Alpha64,
16}
17
18impl StatisticalTest for NormalResidualTest {
19 type Input = (ModelMatrix, DMatrix<f64>, DMatrix<f64>, DMatrix<f64>);
20 type Output = Result<NormalResidualStatistic, Box<dyn std::error::Error>>;
21
22 fn assumptions() -> Vec<Box<dyn Assumption>> {
23 ShapiroWilkTest::assumptions()
24 }
25
26 fn null_hypotheses() -> Vec<Box<dyn Conclusion>> {
27 vec![Box::new(NormalResiduals::new())]
28 }
29
30 fn alternate_hypotheses() -> Vec<Box<dyn Conclusion>> {
31 vec![Box::new(NotNormalResiduals::new())]
32 }
33
34 fn test(&mut self, (x, y, model, w): &Self::Input) -> Self::Output {
35 let x = x.matrix();
36 let mut weighted_x = x.clone();
37 weighted_x
38 .column_iter_mut()
39 .for_each(|mut row| row.component_mul_assign(w));
40 let weighted_y = y.component_mul(w);
41
42 let shapiro_wilk_statistic = ShapiroWilkTest::new().with_alpha(self.alpha).test(
43 &residuals(&weighted_y, &weighted_x, model)
44 .as_slice()
45 .to_vec(),
46 )?;
47 Ok(NormalResidualStatistic {
48 shapiro_wilk_statistic,
49 })
50 }
51}
52
53#[derive(Copy, Clone, Debug)]
54pub struct NormalResidualStatistic {
55 shapiro_wilk_statistic: ShapiroWilkStatistic,
56}
57
58impl Statistic for NormalResidualStatistic {
59 fn alpha(&self) -> Alpha64 {
60 self.shapiro_wilk_statistic.alpha
61 }
62
63 fn statistic(&self) -> f64 {
64 self.shapiro_wilk_statistic.w
65 }
66
67 fn probability_value(&self) -> f64 {
68 self.shapiro_wilk_statistic.p
69 }
70
71 fn conclusion(&self) -> RejectionStatus {
72 let shapiro_wilk_conclusion = self.shapiro_wilk_statistic.conclusion();
73 if shapiro_wilk_conclusion.unwrap().is::<Normal>() {
74 RejectionStatus::RejectInFavorOf(Box::new(NormalResiduals {}))
75 } else {
76 RejectionStatus::FailToReject(Box::new(NotNormalResiduals {}))
77 }
78 }
79}
80
81impl Default for NormalResidualTest {
82 fn default() -> Self {
83 Self { alpha: 0.05.into() }
84 }
85}
86
87impl NormalResidualTest {
88 pub fn new() -> Self {
89 Self::default()
90 }
91
92 pub fn with_alpha<A: Into<Alpha64>>(self, alpha: A) -> Self {
93 Self {
94 alpha: alpha.into(),
95 }
96 }
97}