use crate::linalg::*;
#[derive(Debug)]
pub struct PolynomialRegressor {
pub coef: Vec<f64>,
}
impl PolynomialRegressor {
pub fn new(deg: usize) -> Self {
PolynomialRegressor {
coef: vec![0.; deg + 1],
}
}
fn update(&mut self, params: &[f64]) -> &mut Self {
self.coef = params.to_owned();
self
}
pub fn predict(&self, x: &[f64]) -> Vec<f64> {
x.iter()
.map(|val| {
self.coef
.iter()
.rev()
.fold(0., |acc, coeff| acc * val + coeff)
})
.collect::<Vec<_>>()
}
pub fn fit(&mut self, x: &[f64], y: &[f64]) -> &mut Self {
assert_eq!(x.len(), y.len());
let xv = vandermonde(x, self.coef.len());
let xtx = xtx(&xv, x.len());
let xtxinv = invert_matrix(&xtx);
let xty = matmul(&xv, y, x.len(), y.len(), true, false);
let coeffs = matmul(
&xtxinv,
&xty,
self.coef.len(),
self.coef.len(),
false,
false,
);
self.update(&coeffs)
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx_eq::assert_approx_eq;
#[test]
fn test_slr() {
let x = vec![0., 1., 2., 3., 4., 5., 6., 7., 8., 9.];
let y = vec![5., 7., 9., 11., 13., 15., 17., 19., 21., 23.];
let mut slr = PolynomialRegressor::new(1);
slr.update(&[5., 2.]);
assert_eq!(slr.predict(&x), y);
slr.update(&[0., 1.]);
assert_eq!(slr.predict(&x), x);
}
#[test]
fn test_fits() {
let x: Vec<f64> = (0..250).into_iter().map(|x| x as f64 / 10.).collect();
let yv: Vec<f64> = (&x).into_iter().map(|v| 5. + 2. * v).collect();
let coeffs = [5., 2.];
let mut p = PolynomialRegressor::new(1);
p.fit(&x, &yv);
let coeffs1 = p.coef;
for i in 0..2 {
assert_approx_eq!(coeffs[i], coeffs1[i], 1e-3);
}
}
}