Skip to content

Instantly share code, notes, and snippets.

@jonathan-grah
Created June 2, 2022 06:53
Show Gist options
  • Save jonathan-grah/5951e1bd0a78b0d3dbf66d7802077c75 to your computer and use it in GitHub Desktop.
Save jonathan-grah/5951e1bd0a78b0d3dbf66d7802077c75 to your computer and use it in GitHub Desktop.
A quick script to calculate k-means for predesignated cluster centroids
const { fraction } = require('mathjs');
const clusters = [];
class Cluster {
points = [];
constructor(centroidX, centroidY) {
this.centroidX = centroidX;
this.centroidY = centroidY;
}
clearPoints() {
this.points = [];
}
addPoint(point) {
this.points.push(point);
}
recalculateMean() {
this.centroidX = 0;
this.centroidY = 0;
this.points.forEach(point => {
this.centroidX += point.x;
this.centroidY += point.y;
});
this.centroidX /= this.points.length;
this.centroidY /= this.points.length;
}
}
class DataPoint {
constructor(x, y) {
this.x = x;
this.y = y;
}
}
const assignPointToCluster = point => {
let clusterIndex = null;
clusters.forEach((cluster, index) => {
if (clusterIndex == null) clusterIndex = index;
const prevDistance =
Math.pow(point.x - clusters[clusterIndex].centroidX, 2) +
Math.pow(point.y - clusters[clusterIndex].centroidY, 2);
const newDistance =
Math.pow(point.x - cluster.centroidX, 2) +
Math.pow(point.y - cluster.centroidY, 2);
console.log(`\t\t\tDistance from cluster ${index + 1}: ${newDistance}`);
if (newDistance <= prevDistance) clusterIndex = index;
});
clusters[clusterIndex].addPoint(point);
}
const inputs = [[3,1], [5,1], [4,2], [5,2], [2,5], [7,4], [1,0], [8,0]];
clusters.push(new Cluster(1, 2));
clusters.push(new Cluster(1, 4));
let iterations = 0;
const KMeans = () => {
console.log(`\nK-MEANS ITERATION ${iterations + 1}:\n`);
const prevClusters = JSON.parse(JSON.stringify(clusters));
clusters.forEach(cluster => cluster.clearPoints());
console.log("\tData Points:\n");
inputs.forEach(input => {
console.log(`\t\tData Point: (${input})`);
const point = new DataPoint(input[0], input[1]);
assignPointToCluster(point);
});
console.log(`\n\tRecalculated Centroids:\n`);
clusters.forEach((cluster, index) => {
cluster.recalculateMean();
console.log(`\t\tCluster ${index + 1}: `);
console.log(`\t\t\tCentroid:`,
`(${fraction(cluster.centroidX)},${fraction(cluster.centroidY)})`);
console.log(`\t\t\tPoints:`);
cluster.points.forEach(point => {
console.log(`\t\t\t\t(${point.x},${point.y})`);
});
});
iterations++;
if (iterations == 1) return KMeans();
// Now we check that nothing has changed since the last iteration
let hasAnythingChanged = false;
prevClusters.forEach((prevCluster, index) => {
if (JSON.stringify(prevCluster.points) !== JSON.stringify(clusters[index].points))
hasAnythingChanged = true;
});
if (!hasAnythingChanged) {
console.log(`\n\n\nCOMPLETED IN ${iterations} ITERATIONS`);
return;
} else return KMeans();
};
KMeans();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment