UNPKG

spectral-clustering-js

Version:
95 lines (94 loc) 4.04 kB
"use strict"; 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;