npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

pca-js

v2.0.6

Published

Principal Components Analysis in javascript

Readme

New in v2 🌟

Massive changes in v2:

  • Rewrote the entire codebase to typescript
  • Rollup to UMD, CJS and ESM whichever you prefer
  • Test cases to prevent regressions and keep behavior consistent
  • Package level tests in order to prevent downstream breakages on dependents

Bugfixes: Closed the 4 open issues with chonky fixes. Closed all PRs since full rewrite. Basically optimized matmul ops now to prevent OOMs and optimized build processes to port typescript. Feel free to Open new Issues/PRs as required.

How to use now: Same as below except for the changed CDN links. I've inline edited the older readme in order to accurately reflect links and examples using modern ESM/UMD/CJS.

Principal Components Analysis in Javascript!

Node:

  • 🛠️ Node.js CommonJS: const PCA = require('pca-js')
  • 🛠️ Node.js ESM: import PCA from 'pca-js'

Browser:

Always get the updated CDN links at JSDELIVR. The below are just provided as an example.

  • 🌍 Browser (Global): <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/pca.min.js"></script>window.PCA
  • 🌍 Browser (ESM): import PCA from 'https://cdn.jsdelivr.net/npm/[email protected]/+esm'

Can use unpkg etc but only jsdelivr is actively supported and tested by me. Open an issue for better CDN recommends.

How to use the API

All methods are exposed through PCA global variable

Say you have data for marks of a class 4 students in 3 examinations on the same subject:

Student 1: 40,50,60
Student 2: 50,70,60
Student 3: 80,70,90
Student 4: 50,60,80

You want to examine whether it is possible to come up with a single descriptive set of scores which explains performance across the class. Alternatively, whether it would make sense to replace 3 exams with just one (and reduce stress on students).

First get the set of eigenvectors and eigenvalues (principal components and adjusted loadings)

var data = [[40,50,60],[50,70,60],[80,70,90],[50,60,80]];
var vectors = PCA.getEigenVectors(data);
//Outputs 
// [{
//     "eigenvalue": 520.0992658908312,
//     "vector": [0.744899700771276, 0.2849796479974595, 0.6032503924724023]
// }, {
//     "eigenvalue": 78.10455398035167,
//     "vector": [0.2313199078283626, 0.7377809866160473, -0.6341689964277106]
// }, {
//     "eigenvalue": 18.462846795484058,
//     "vector": [0.6257919271076777, -0.6119361208615616, -0.4836513702572988]
// }]

Now you'd need to find a set of eigenvectors that would explain a decent amount of variance across your exams (thus telling you if 1 test or 2 tests would suffice instead of three)

var first = PCA.computePercentageExplained(vectors,vectors[0])
// 0.8434042149581044
var topTwo = PCA.computePercentageExplained(vectors,vectors[0],vectors[1])
// 0.9700602484397556

So if you wanted to have 97% certainty, that someone wouldn't just flunk out accidentally, you'd take 2 exams. But let's say you just wanted to take 1, explaining 84% of variance is good enough. And instead of taking the examination again, you just wanted a normalized score

var adData = PCA.computeAdjustedData(data,vectors[0])
// {
//     "adjustedData": [
//         [-22.27637101744241, -9.127781049780463, 31.316721747529886, 0.08743031969298887]
//     ],
//     "formattedAdjustedData": [
//         [-22.28, -9.13, 31.32, 0.09]
//     ],
//     "avgData": [
//         [-55, -62.5, -72.5],
//         [-55, -62.5, -72.5],
//         [-55, -62.5, -72.5],
//         [-55, -62.5, -72.5]
//     ],
//     "selectedVectors": [
//         [0.744899700771276, 0.2849796479974595, 0.6032503924724023]
//     ]
// }

The adjustedData is centered (mean = 0), but you could always set the mean to something like 50, to get scores of [-22.27637101744241, -9.127781049780463, 31.316721747529886, 0.08743031969298887].map(score=>Math.round(score+50)) equal to [28, 41, 81, 50] , and that's how well your students would have done, in the order of students.

Other cool stuff that's possible

Compression (lossy):

var compressed = adData.formattedAdjustedData;
//[
//         [-22.28, -9.13, 31.32, 0.09]
//     ]
var uncompressed = PCA.computeOriginalData(compressed,adData.selectedVectors,adData.avgData);
//uncompressed.formattedOriginalData (lossy since 2 eigenvectors are removed)
// [
//     [38.4, 56.15, 59.06],
//     [48.2, 59.9, 66.99],
//     [78.33, 71.43, 91.39],
//     [55.07, 62.53, 72.55]
// ]

Compare this to the original data to understand just how lossy the compression was

//Original Data
[
    [40, 50, 60],
    [50, 70, 60],
    [80, 70, 90],
    [50, 60, 80]
]
//Uncompressed Data
[
    [38.4, 56.15, 59.06],
    [48.2, 59.9, 66.99],
    [78.33, 71.43, 91.39],
    [55.07, 62.53, 72.55]
]

List of Methods

computeDeviationMatrix(data)

Find centered matrix from original data

computeDeviationScores(centeredMatrix)

Find deviation from mean for values in matrix

computeSVD(deviationScores)

Singular Value Decomposition of matrix

computePercentageExplained(allvectors, ...selected)

Find the cumulative percentage explained variance by selected vectors (select vectors accordingly to view specific explained variance)

computeOriginalData(compressedData,selectedVectors,avgData)

Get original data from the adjusted data after selecting a few eigenvectors

computeVarianceCovariance(devSumOfSquares,isSample)

Get variance covariance matrix from the data, adjust n by one if the data is from a sample

computeAdjustedData(initialData, ...selectedVectors)

Get adjusted data using principal components as selected

getEigenVectors(initialData)

Get the principal components of data using the steps outlined above.

analyseTopResult(initialData)

Same as computeAdjustedData(initialData,vectors[0]). Selecting only the top eigenvector which explains the most variance.

License (has been changed following v2.0 release)

This license will remain consistent going forward with subsequent versions. The license is GPL which is a better license imho for copyleft and packages that might derive this package. Was MIT earlier. LICENSE: GPL 3.0