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)` |
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)` |
33 | 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 | |