spectral-clustering-js
Version:
Spectral clustering package for TypeScript
95 lines (94 loc) • 4.04 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
var Utils_1 = require("./Utils");
/**
* This class can find the clustering for n clusters OR
* can find the best clustering for [2..n] clusters
*/
var Kmeans1D = /** @class */ (function () {
function Kmeans1D(items) {
this.items = items;
this.min = Utils_1.Utils.min(items);
this.max = Utils_1.Utils.max(items);
this.amplitude = this.max - this.min;
}
Kmeans1D.prototype.findBestClustering = function (maxClusters) {
if (maxClusters === void 0) { maxClusters = 10; }
var results = [];
var distances = [];
for (var c = 2; c < maxClusters; c++) {
var tmpResult = this.tryToCluster(c);
var affectations = tmpResult[0];
var centroids = tmpResult[1];
// calculate the total distance from points to centroids
var totalDistance = 0;
for (var i = 0; i < affectations.length; i++) {
totalDistance += Math.abs(this.items[i] - centroids[affectations[i]]);
}
results.push(affectations);
distances.push(totalDistance);
}
// elbow method to find the best cluster size
var curveDifference = [];
for (var d = 1; d < distances.length - 1; d++) {
var descending1 = distances[d] - distances[d - 1];
var descending2 = distances[d + 1] - distances[d];
curveDifference.push(descending2 - descending1);
}
var bestCurveDifference = Utils_1.Utils.maxIndex(curveDifference);
return results[bestCurveDifference + 1];
};
/**
* Finds the best clustering with $nbClusters clusters
* @param nbClusters
* @returns [centroidsForPoints, centroids]
*/
Kmeans1D.prototype.tryToCluster = function (nbClusters) {
var centroids = [];
var pointsForCentroid = [];
// initialize the centroids randomly
for (var i = 0; i < nbClusters; i++) {
centroids.push(this.min + Math.random() * this.amplitude);
pointsForCentroid.push([]);
}
var centroidForPoints = [];
var previousCentroidForPoints = [];
// iterate until the cluster do not move anymore
var stillMoving = true;
var nbIterations = 0;
while (nbIterations < 50 && stillMoving) {
stillMoving = false;
centroidForPoints = [];
// associate each point to the nearest centroid
for (var itemIndex = 0; itemIndex < this.items.length; itemIndex++) {
var itemValue = this.items[itemIndex];
var minDistance = Number.MAX_VALUE;
var nearestCentroid = 0;
for (var centroidIndex = 0; centroidIndex < centroids.length; centroidIndex++) {
var d = Math.abs(centroids[centroidIndex] - itemValue);
if (d < minDistance || minDistance == null) {
minDistance = d;
nearestCentroid = centroidIndex;
}
}
centroidForPoints.push(nearestCentroid);
// if something has changed since the previous iteration, we need one more iteration
if (previousCentroidForPoints.length < itemIndex
|| previousCentroidForPoints[itemIndex] != nearestCentroid) {
stillMoving = true;
}
pointsForCentroid[nearestCentroid].push(itemValue);
}
previousCentroidForPoints = centroidForPoints;
// move the centroids to the center of items
for (var c = 0; c < centroids.length; c++) {
centroids[c] = Utils_1.Utils.mean(pointsForCentroid[c]);
pointsForCentroid[c] = [];
}
nbIterations++;
}
return [centroidForPoints, centroids];
};
return Kmeans1D;
}());
exports.Kmeans1D = Kmeans1D;