Skip to content

Commit

Permalink
Add option to seed the randomness for predictable results
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisbubernak committed Aug 24, 2021
1 parent c1c1abf commit 9d07492
Show file tree
Hide file tree
Showing 5 changed files with 23 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ kmeans.clusterize(vectors, {k: 4}, (err,res) => {
- **options** object:
- **k** : number of clusters
- **distance** (optional) : custom distance function returning the distance between two points `(a,b) => number`, *default* Euclidian Distance
- **seed** (optional) : value that can be provided to get repeatable cluster generation
- **callback** node-style callback taking error and result argument

## Outputs
Expand Down
5 changes: 4 additions & 1 deletion lib/kmeans.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The kmeans will return an error if:
- The number of different input vectors is smaller than k
*/

const seedrandom = require('seedRandom');
const _ = require('underscore');


Expand Down Expand Up @@ -60,7 +61,8 @@ class Group {
if (this.centroid && this.cluster.length > 0) {
this.calculateCentroid();
} else { // random selection
const i = Math.floor(Math.random() * self.indexes.length);
const rand = self.seed == null ? Math.random() : seedrandom(self.seed)();
const i = Math.floor(rand * self.indexes.length);
this.centroidIndex = self.indexes[i];
self.indexes.splice(i, 1);
this.centroid = [];
Expand Down Expand Up @@ -126,6 +128,7 @@ class Clusterize {
this.options = options;
this.v = this.checkV(vector);
this.k = this.options.k;
this.seed = this.options.seed;
this.distanceFunction = this.options.distance || euclidianDistance;
if (this.v.length < this.k) {
const errMessage = `The number of points must be greater than
Expand Down
5 changes: 5 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"node": ">= v0.6.0"
},
"dependencies": {
"seedrandom": "^3.0.5",
"underscore": "^1.9.1"
},
"devDependencies": {
Expand Down
12 changes: 12 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,17 @@ describe('kmeans', () => {
done();
});
});

it('should produce consistent output when a seed is provided', (done) => {
kmeans.clusterize(data3D, { k: 3, seed: 42 }, (err, res) => {

// Verify first value of each centroid is always the same
const cs = res.map(r => r.centroid[0]);
cs[0].should.equal(202.6);
cs[1].should.equal(-10.15);
cs[2].should.equal(39.75);
done();
});
});
});
});

0 comments on commit 9d07492

Please sign in to comment.