UNPKG

ml-matrix

Version:

Matrix manipulation and computation library

89 lines (77 loc) 2.3 kB
import { isAnyArray } from 'is-any-array'; import Matrix from '../matrix'; import WrapperMatrix2D from '../wrap/WrapperMatrix2D'; export default class nipals { constructor(X, options = {}) { X = WrapperMatrix2D.checkMatrix(X); let { Y } = options; const { scaleScores = false, maxIterations = 1000, terminationCriteria = 1e-10, } = options; let u; if (Y) { if (isAnyArray(Y) && typeof Y[0] === 'number') { Y = Matrix.columnVector(Y); } else { Y = WrapperMatrix2D.checkMatrix(Y); } if (Y.rows !== X.rows) { throw new Error('Y should have the same number of rows as X'); } u = Y.getColumnVector(0); } else { u = X.getColumnVector(0); } let diff = 1; let t, q, w, tOld; for ( let counter = 0; counter < maxIterations && diff > terminationCriteria; counter++ ) { w = X.transpose().mmul(u).div(u.transpose().mmul(u).get(0, 0)); w = w.div(w.norm()); t = X.mmul(w).div(w.transpose().mmul(w).get(0, 0)); if (counter > 0) { diff = t.clone().sub(tOld).pow(2).sum(); } tOld = t.clone(); if (Y) { q = Y.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0)); q = q.div(q.norm()); u = Y.mmul(q).div(q.transpose().mmul(q).get(0, 0)); } else { u = t; } } if (Y) { let p = X.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0)); p = p.div(p.norm()); let xResidual = X.clone().sub(t.clone().mmul(p.transpose())); let residual = u.transpose().mmul(t).div(t.transpose().mmul(t).get(0, 0)); let yResidual = Y.clone().sub( t.clone().mulS(residual.get(0, 0)).mmul(q.transpose()), ); this.t = t; this.p = p.transpose(); this.w = w.transpose(); this.q = q; this.u = u; this.s = t.transpose().mmul(t); this.xResidual = xResidual; this.yResidual = yResidual; this.betas = residual; } else { this.w = w.transpose(); this.s = t.transpose().mmul(t).sqrt(); if (scaleScores) { this.t = t.clone().div(this.s.get(0, 0)); } else { this.t = t; } this.xResidual = X.sub(t.mmul(w.transpose())); } } }