Skip to content

Instantly share code, notes, and snippets.

@tarunc
Created July 19, 2012 08:58
Show Gist options
  • Save tarunc/3141694 to your computer and use it in GitHub Desktop.
Save tarunc/3141694 to your computer and use it in GitHub Desktop.
K-Means Clustering in Javascript
var distances = {
euclidean: function(v1, v2) {
var total = 0;
for (var i = 0; i < v1.length; i++) {
total += Math.pow(v2[i] - v1[i], 2);
}
return Math.sqrt(total);
},
manhattan: function(v1, v2) {
var total = 0;
for (var i = 0; i < v1.length ; i++) {
total += Math.abs(v2[i] - v1[i]);
}
return total;
},
max: function(v1, v2) {
var max = 0;
for (var i = 0; i < v1.length; i++) {
max = Math.max(max , Math.abs(v2[i] - v1[i]));
}
return max;
}
};
function randomCentroids(points, k) {
var centroids = points.slice(0); // copy
centroids.sort(function() {
return (Math.round(Math.random()) - 0.5);
});
return centroids.slice(0, k);
}
function closestCentroid(point, centroids, distance) {
var min = Infinity,
index = 0;
for (var i = 0; i < centroids.length; i++) {
var dist = distance(point, centroids[i]);
if (dist < min) {
min = dist;
index = i;
}
}
return index;
}
function kmeans(points, k, distance, snapshotPeriod, snapshotCb) {
k = k || Math.max(2, Math.ceil(Math.sqrt(points.length / 2)));
distance = distance || "euclidean";
if (typeof distance == "string") {
distance = distances[distance];
}
var centroids = randomCentroids(points, k);
var assignment = new Array(points.length);
var clusters = new Array(k);
var iterations = 0;
var movement = true;
while (movement) {
// update point-to-centroid assignments
for (var i = 0; i < points.length; i++) {
assignment[i] = closestCentroid(points[i], centroids, distance);
}
// update location of each centroid
movement = false;
for (var j = 0; j < k; j++) {
var assigned = [];
for (var i = 0; i < assignment.length; i++) {
if (assignment[i] == j) {
assigned.push(points[i]);
}
}
if (!assigned.length) {
continue;
}
var centroid = centroids[j];
var newCentroid = new Array(centroid.length);
for (var g = 0; g < centroid.length; g++) {
var sum = 0;
for (var i = 0; i < assigned.length; i++) {
sum += assigned[i][g];
}
newCentroid[g] = sum / assigned.length;
if (newCentroid[g] != centroid[g]) {
movement = true;
}
}
centroids[j] = newCentroid;
clusters[j] = assigned;
}
if (snapshotCb && (iterations++ % snapshotPeriod == 0)) {
snapshotCb(clusters);
}
}
return clusters;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment