1 //! Regression analysis
2 
3 use crate::stats::bivariate::Data;
4 use crate::stats::float::Float;
5 
6 /// A straight line that passes through the origin `y = m * x`
7 #[derive(Clone, Copy)]
8 pub struct Slope<A>(pub A)
9 where
10     A: Float;
11 
12 impl<A> Slope<A>
13 where
14     A: Float,
15 {
16     /// Fits the data to a straight line that passes through the origin using ordinary least
17     /// squares
18     ///
19     /// - Time: `O(length)`
fit(data: &Data<'_, A, A>) -> Slope<A>20     pub fn fit(data: &Data<'_, A, A>) -> Slope<A> {
21         let xs = data.0;
22         let ys = data.1;
23 
24         let xy = crate::stats::dot(xs, ys);
25         let x2 = crate::stats::dot(xs, xs);
26 
27         Slope(xy / x2)
28     }
29 
30     /// Computes the goodness of fit (coefficient of determination) for this data set
31     ///
32     /// - Time: `O(length)`
r_squared(&self, data: &Data<'_, A, A>) -> A33     pub fn r_squared(&self, data: &Data<'_, A, A>) -> A {
34         let _0 = A::cast(0);
35         let _1 = A::cast(1);
36         let m = self.0;
37         let xs = data.0;
38         let ys = data.1;
39 
40         let n = A::cast(xs.len());
41         let y_bar = crate::stats::sum(ys) / n;
42 
43         let mut ss_res = _0;
44         let mut ss_tot = _0;
45 
46         for (&x, &y) in data.iter() {
47             ss_res = ss_res + (y - m * x).powi(2);
48             ss_tot = ss_res + (y - y_bar).powi(2);
49         }
50 
51         _1 - ss_res / ss_tot
52     }
53 }
54