UNPKG

nipals-pca

Version:

A NIPALS implementation of PCA that handles missing values

69 lines (46 loc) 1.65 kB
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;