Created
June 2, 2022 06:53
-
-
Save jonathan-grah/5951e1bd0a78b0d3dbf66d7802077c75 to your computer and use it in GitHub Desktop.
A quick script to calculate k-means for predesignated cluster centroids
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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