nipals-pca
Version:
A NIPALS implementation of PCA that handles missing values
169 lines (124 loc) • 4.83 kB
JavaScript
const chai = require('chai');
const {expect} = chai;
chai.use(require('chai-roughly'));
const { Dataset } = require('../src/Dataset');
const { PCA } = require('../src/pca');
var fs = require('fs');
var Papa = require('papaparse');
let inputMatrixPromise = parseExampleInput(fs.readFileSync('test/data/iris/IRIS.csv', 'utf8'));
let mv_inputMatrixPromise = parseExampleInput(fs.readFileSync('test/data/iris/IRIS_mv.csv', 'utf8'));
let meansPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisMeanValues.txt', 'utf8'));
let stdPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisStdValues.txt', 'utf8'));
let mv_meansPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisMeanValues_mv.txt', 'utf8'));
let mv_stdPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisStdValues_mv.txt', 'utf8'));
let scoresPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisT.txt', 'utf8'));
let loadingsPromise = parseExampleInput(fs.readFileSync('test/data/iris/irisP.txt', 'utf8'));
let mv_scoresPromise = parseExampleInput(fs.readFileSync('test/data/iris/IRIS_mv_T.txt', 'utf8'));
let mv_loadingsPromise = parseExampleInput(fs.readFileSync('test/data/iris/IRIS_mv_P.txt', 'utf8'));
describe('Test Dataset creation and normalization', () => {
let jsonMatrix;
let means,stdevs;
let dataset;
before(async ()=>{
jsonMatrix = await inputMatrixPromise;
//remove last column
jsonMatrix.map(r => r.splice(-1, 1));
dataset = new Dataset(jsonMatrix);
means = await meansPromise;
means = means.map(([value])=>parseFloat(value));
stdevs = await stdPromise;
stdevs = stdevs.map(([value])=>1/parseFloat(value))
});
it('Check centering', () => {
expect(means).to.roughly(0.001).deep.equal(dataset.means);
})
it('Check UV scaling', () => {
expect(stdevs).to.roughly(0.001).deep.equal(dataset.stdevs);
})
})
describe('Test PCA', () => {
let pca;
before(async ()=>{
let components = 3;
jsonMatrix = await parseExampleInput(fs.readFileSync('test/data/iris/IRIS.csv', 'utf8'));
//remove last column
jsonMatrix.map(r => r.splice(-1, 1));
dataset = new Dataset(jsonMatrix);
scores = await scoresPromise;
scores.splice(0,1);
scores = scores.map(row=>row.slice(1,components+1).map(v=>parseFloat(v)));
loadings = await loadingsPromise;
loadings.splice(0,1);
loadings = loadings.map(row=>row.slice(1,components+1).map(v=>parseFloat(v)));
pca = new PCA(dataset,components);
pca.fit();
});
let tolerance = 0.001;
for (let a = 0; a < 3; a++) {
it(`PCA loadings and scores are correct for component ${a+1}`, () => {
expect(loadings.map(row=>Math.abs(row[a]))).to.roughly(tolerance).deep.equal(pca.P.getColumn(a).map(v=>Math.abs(v)));
expect(scores.map(row=>Math.abs(row[a]))).to.roughly(tolerance).deep.equal(pca.T.getColumn(a).map(v=>Math.abs(v)));
})
}
})
describe('Test MV Dataset creation and normalization', () => {
let jsonMatrix;
let means,stdevs;
let dataset;
before(async ()=>{
jsonMatrix = await mv_inputMatrixPromise;
//remove last column
jsonMatrix.map(r => r.splice(0, 1));
dataset = new Dataset(jsonMatrix);
means = await mv_meansPromise;
means = means.map(([value])=>parseFloat(value));
stdevs = await mv_stdPromise;
stdevs = stdevs.map(([value])=>1/parseFloat(value))
});
it('Check centering', () => {
expect(means).to.roughly(0.001).deep.equal(dataset.means);
})
it('Check UV scaling', () => {
expect(stdevs).to.roughly(0.001).deep.equal(dataset.stdevs);
})
})
describe('Test PCA with missing values', () => {
let pca;
before(async ()=>{
let components = 3;
jsonMatrix = await parseExampleInput(fs.readFileSync('test/data/iris/IRIS_mv.csv', 'utf8'));
//remove last column
jsonMatrix.map(r => r.splice(0, 1));
dataset = new Dataset(jsonMatrix);
scores = await mv_scoresPromise;
scores.splice(0,1);
scores = scores.map(row=>row.slice(1,components+1).map(v=>parseFloat(v)));
loadings = await mv_loadingsPromise;
loadings.splice(0,1);
loadings = loadings.map(row=>row.slice(1,components+1).map(v=>parseFloat(v)));
pca = new PCA(dataset,components);
pca.fit();
});
let tolerance = 0.001;
for (let a = 0; a < 3; a++) {
it(`PCA loadings and scores are correct for component ${a+1}`, () => {
expect(loadings.map(row=>Math.abs(row[a]))).to.roughly(tolerance).deep.equal(pca.P.getColumn(a).map(v=>Math.abs(v)));
expect(scores.map(row=>Math.abs(row[a]))).to.roughly(tolerance).deep.equal(pca.T.getColumn(a).map(v=>Math.abs(v)));
})
}
})
function parseExampleInput(file) {
return new Promise((resolve, reject) => {
Papa.parse(file, {
header: false,
skipEmptyLines: true,
complete(results, file) {
let data = results.data;
resolve(data)
},
error(err, file) {
reject(err)
}
})
})
}