nipals-pca
Version:
A NIPALS implementation of PCA that handles missing values
69 lines (46 loc) • 1.65 kB
JavaScript
const { Matrix } = require('ml-matrix');
class PCA {
constructor(dataset, A) {
this.dataset = dataset;
this.A = A;
this.T = new Matrix(this.dataset.matrix.rows, this.A);
this.P = new Matrix(this.dataset.matrix.columns, this.A);
}
fit() {
let dCrit = 1e-20
let maxIter = 1000
let iter = 0
let diff = 1
for (let a = 0; a < this.A; a++) {
iter = 0
diff = 1
this.T.setColumn(a, this.dataset.matrix.getColumn(0))
while (diff > dCrit && iter < maxIter) {
diff = this.nipals(a)
iter += 1
}
this.dataset.matrix.subtract(this.T.getColumnVector(a).mmul(this.P.getColumnVector(a).transpose()))
this.dataset.setZeroes()
}
}
nipals(a) {
let t = this.T.getColumnVector(a)
let p = this.dataset.matrix.transpose().mmul(t)
if(this.dataset.missing){
let pcorr = t.clone().pow(2).transpose().mmul(this.dataset.xmask).transpose();
let zvals = pcorr.getColumn(0).map((v, i) => v == 0 ? i : -1).filter(idx => idx >= 0);
p.divColumnVector(pcorr);
zvals.map(zidx=>p.set(zidx,0,0));
}
p.div(p.norm())
let tnew = this.dataset.matrix.mmul(p)
if(this.dataset.missing){
tnew.divColumnVector(this.dataset.xmask.mmul(p.clone().pow(2)))
}
let diff = t.subColumnVector(tnew).pow(2).sum();
this.T.setColumn(a, tnew)
this.P.setColumn(a, p)
return diff;
}
}
module.exports.PCA = PCA;