mirror of
https://github.com/Magnus167/rustframe.git
synced 2025-08-20 04:00:01 +00:00
Add PCA implementation with fit and transform methods
This commit is contained in:
parent
b6645fcfbd
commit
d4c0f174b1
85
src/compute/models/pca.rs
Normal file
85
src/compute/models/pca.rs
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
use crate::matrix::{Matrix, SeriesOps};
|
||||||
|
use rand;
|
||||||
|
|
||||||
|
/// Returns the `n_components` principal axes (rows) and the centred data’s mean.
|
||||||
|
pub struct PCA {
|
||||||
|
pub components: Matrix<f64>, // (n_components, n_features)
|
||||||
|
pub mean: Matrix<f64>, // (1, n_features)
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PCA {
|
||||||
|
pub fn fit(x: &Matrix<f64>, n_components: usize, iters: usize) -> Self {
|
||||||
|
let m = x.rows();
|
||||||
|
let n = x.cols();
|
||||||
|
assert!(n_components <= n);
|
||||||
|
|
||||||
|
// ----- centre data -----
|
||||||
|
let mean_vec = {
|
||||||
|
let mut v = Matrix::zeros(1, n);
|
||||||
|
for j in 0..n {
|
||||||
|
let mut s = 0.0;
|
||||||
|
for i in 0..m {
|
||||||
|
s += x[(i, j)];
|
||||||
|
}
|
||||||
|
v[(0, j)] = s / m as f64;
|
||||||
|
}
|
||||||
|
v
|
||||||
|
};
|
||||||
|
let x_centered = x - &mean_vec;
|
||||||
|
|
||||||
|
// ----- covariance matrix C = Xᵀ·X / (m-1) -----
|
||||||
|
let cov = x_centered.transpose().dot(&x_centered) * (1.0 / (m as f64 - 1.0));
|
||||||
|
|
||||||
|
// ----- power iteration to find top eigenvectors -----
|
||||||
|
let mut comp = Matrix::zeros(n_components, n);
|
||||||
|
let mut b = Matrix::zeros(1, n); // current vector
|
||||||
|
for c in 0..n_components {
|
||||||
|
// random initial vector
|
||||||
|
for j in 0..n {
|
||||||
|
b[(0, j)] = rand::random::<f64>() - 0.5;
|
||||||
|
}
|
||||||
|
// subtract projections on previously found components
|
||||||
|
for prev in 0..c {
|
||||||
|
// let proj = b.dot(Matrix::from_vec(data, rows, cols).transpose())[(0, 0)];
|
||||||
|
// let proj = b.dot(&comp.row(prev).transpose())[(0, 0)];
|
||||||
|
let proj = b.dot(&Matrix::from_vec(comp.row(prev).to_vec(), 1, n).transpose())[(0, 0)];
|
||||||
|
// subtract projection to maintain orthogonality
|
||||||
|
for j in 0..n {
|
||||||
|
b[(0, j)] -= proj * comp[(prev, j)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// iterate
|
||||||
|
for _ in 0..iters {
|
||||||
|
// b = C·bᵀ
|
||||||
|
let mut nb = cov.dot(&b.transpose()).transpose();
|
||||||
|
// subtract projections again to maintain orthogonality
|
||||||
|
for prev in 0..c {
|
||||||
|
let proj = nb.dot(&Matrix::from_vec(comp.row(prev).to_vec(), 1, n).transpose())[(0, 0)];
|
||||||
|
for j in 0..n {
|
||||||
|
nb[(0, j)] -= proj * comp[(prev, j)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// normalise
|
||||||
|
let norm = nb.data().iter().map(|v| v * v).sum::<f64>().sqrt();
|
||||||
|
for j in 0..n {
|
||||||
|
nb[(0, j)] /= norm;
|
||||||
|
}
|
||||||
|
b = nb;
|
||||||
|
}
|
||||||
|
// store component
|
||||||
|
for j in 0..n {
|
||||||
|
comp[(c, j)] = b[(0, j)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
components: comp,
|
||||||
|
mean: mean_vec,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Project new data on the learned axes.
|
||||||
|
pub fn transform(&self, x: &Matrix<f64>) -> Matrix<f64> {
|
||||||
|
let x_centered = x - &self.mean;
|
||||||
|
x_centered.dot(&self.components.transpose())
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user