nathanepstein / clusters Goto Github PK
View Code? Open in Web Editor NEWk-means clustering in Javascript
k-means clustering in Javascript
How did you generate the png files, or some kind of visual representation?
Did you use a library to display the point data? if so which one, I would like to make a similar visual representation.
Many thanks,
Vince
[ { centroid: [ 10.5, 11 ], points: [ [Object], [Object] ] }, { centroid: [ -9.5, 10.5 ], points: [ [Object], [Object] ] }, { centroid: [ 0.3333333333333333, 0.3333333333333333 ], points: [ [Object], [Object], [Object] ] } ]
How can I remove this [Object] in the points
Hi,
This tool is amazing! Quick question for you. I am using this to cluster groups of students based on their performance. Your tool produces the clusters exactly as I would hope. However, it doesn't seem to output which data indexes are in each cluster, just the data itself. What I'd like is for each cluster output to include an array of the indexes contained, so that I can easily say "Cluster 1 includes students 1, 4, and 8", Cluster 2 includes students 2, 3, and 6", etc.
Is that possible? Or do I have to do a lookup after the fact to reconnect the original data index with the cluster outputs based on the points array?
Thanks,
John
Hey Nathan, not necessarily an issue...but a few amendments to your code. It was quite helpful.
let clusterMaker = {
data: getterSetter([], (arrayOfArrays) => {
let n = arrayOfArrays[0].length;
return (arrayOfArrays.map((array) => {
return array.length === n;
}).reduce((boolA, boolB) => {
return (boolA & boolB)
}, true));
}),
clusters: function () {
let pointsAndCentroids = kmeans(this.data(), {k: this.k(), iterations: this.iterations()});
let points = pointsAndCentroids.points;
let centroids = pointsAndCentroids.centroids;
let centroidReturnValue = centroids.map((centroid) => {
return {
centroid: centroid.location(),
points: points.filter((point) => {
return point.label() === centroid.label()
}).map((point) => {
return point.location()
}),
};
});
centroidReturnValue.map((centroid, key) => {
console.log("Cluster", key ," ", centroid);
});
return centroidReturnValue
},
k: getterSetter(undefined, (value) => {
return ((value % 1 === 0) & (value > 0))
}),
iterations: getterSetter(Math.pow(10, 3), (value) => {
return ((value % 1 === 0) & (value > 0))
}),
};
function kmeans(data, config) {
// default k
let k = config.k || Math.round(Math.sqrt(data.length / 2));
let iterations = config.iterations;
// initialize point objects with data
let points = data.map((vector) => {
return new Point(vector)
});
// intialize centroids randomly
let centroids = [];
for (let i = 0; i < k; i++) {
centroids.push(new Centroid(points[i % points.length].location(), i));
}
// update labels and centroid locations until convergence
for (let iter = 0; iter < iterations; iter++) {
points.forEach((point) => {
point.updateLabel(centroids)
});
centroids.forEach((centroid) => {
centroid.updateLocation(points)
});
}
// return points and centroids
return {
points: points,
centroids: centroids
};
}
// objects
function Point(location) {
let self = this;
this.location = getterSetter(location);
this.label = getterSetter();
this.updateLabel = (centroids) => {
let distancesSquared = centroids.map((centroid) => {
return sumOfSquareDiffs(self.location(), centroid.location());
});
self.label(mindex(distancesSquared));
};
}
function Centroid(initialLocation, label) {
let self = this;
this.location = getterSetter(initialLocation);
this.label = getterSetter(label);
this.updateLocation = (points) => {
let pointsWithThisCentroid = points.filter((point) => {
return point.label() === self.label()
});
if (pointsWithThisCentroid.length > 0) self.location(averageLocation(pointsWithThisCentroid));
};
}
// convenience functions
function getterSetter(initialValue, validator) {
let thingToGetSet = initialValue;
let isValid = validator || function (val) {
return true
};
return (newValue) => {
if (typeof newValue === 'undefined') return thingToGetSet;
if (isValid(newValue)) thingToGetSet = newValue;
};
}
function sumOfSquareDiffs(oneVector, anotherVector) {
let squareDiffs = oneVector.map((component, i) => {
return Math.pow(component - anotherVector[i], 2);
});
return squareDiffs.reduce((a, b) => {
return a + b
}, 0);
}
function mindex(array) {
let min = array.reduce((a, b) => {
return Math.min(a, b);
});
return array.indexOf(min);
}
function sumVectors(a, b) {
return a.map((val, i) => {
return val + b[i]
});
}
function averageLocation(points) {
let zeroVector = points[0].location().map(() => {
return 0
});
let locations = points.map((point) => {
return point.location()
});
let vectorSum = locations.reduce((a, b) => {
return sumVectors(a, b)
}, zeroVector);
return vectorSum.map((val) => {
return val / points.length
});
}
//number of clusters, defaults to undefined
clusterMaker.k(3);
//number of iterations (higher number gives more time to converge), defaults to 1000
clusterMaker.iterations(750);
//data from which to identify clusters, defaults to []
clusterMaker.data([[1, 0], [0, 1], [0, 0], [-10, 10], [-9, 11], [10, 10], [11, 12]]);
clusterMaker.clusters();
Instead of returning the points of the original matrix, I would like it to return an array of indexes pointing to the original points in the matrix. Here is 1 way you can do it
clusters: function() {
var pointsAndCentroids = kmeans(this.data(), {k: this.k(), iterations: this.iterations() });
var points = pointsAndCentroids.points;
var centroids = pointsAndCentroids.centroids;
return centroids.map(function(centroid) {
return points.reduce(function(acc, point, i) {
if (point.label() == centroid.label()) {
acc.push(i);
}
return acc;
}, []);
});
},
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.