sdbscan
Version:
DBSCAN implementation (density based spatial clustering) for javascript. Works in node and browser
236 lines (205 loc) • 6.63 kB
JavaScript
;
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
(function e(t, n, r) {
function s(o, u) {
if (!n[o]) {
if (!t[o]) {
var a = typeof require == "function" && require;if (!u && a) return a(o, !0);if (i) return i(o, !0);var f = new Error("Cannot find module '" + o + "'");throw f.code = "MODULE_NOT_FOUND", f;
}var l = n[o] = { exports: {} };t[o][0].call(l.exports, function (e) {
var n = t[o][1][e];return s(n ? n : e);
}, l, l.exports, e, t, n, r);
}return n[o].exports;
}var i = typeof require == "function" && require;for (var o = 0; o < r.length; o++) {
s(r[o]);
}return s;
})({ 1: [function (require, module, exports) {
"use strict";
(function ($) {
var sdbscan = require("./main.js");
$.sdbscan = sdbscan;
})(window);
}, { "./main.js": 3 }], 2: [function (require, module, exports) {
module.exports = {
/**
* Euclidean distance
*/
eudist: function eudist(v1, v2, sqrt) {
var len = v1.length;
var sum = 0;
for (var i = 0; i < len; i++) {
var d = (v1[i] || 0) - (v2[i] || 0);
sum += d * d;
}
// Square root not really needed
return sqrt ? Math.sqrt(sum) : sum;
},
mandist: function mandist(v1, v2, sqrt) {
var len = v1.length;
var sum = 0;
for (var i = 0; i < len; i++) {
sum += Math.abs((v1[i] || 0) - (v2[i] || 0));
}
// Square root not really needed
return sqrt ? Math.sqrt(sum) : sum;
},
/**
* Unidimensional distance
*/
dist: function dist(v1, v2, sqrt) {
var d = Math.abs(v1 - v2);
return sqrt ? d : d * d;
}
};
}, {}], 3: [function (require, module, exports) {
var Distance = require("./distance.js"),
eudist = Distance.eudist;
/*
DBSCAN(D, epsilon, min_points):
C = 0
for each unvisited point P in dataset
mark P as visited
sphere_points = regionQuery(P, epsilon)
if sizeof(sphere_points) < min_points
ignore P
else
C = next cluster
expandCluster(P, sphere_points, C, epsilon, min_points)
expandCluster(P, sphere_points, C, epsilon, min_points):
add P to cluster C
for each point P’ in sphere_points
if P’ is not visited
mark P’ as visited
sphere_points’ = regionQuery(P’, epsilon)
if sizeof(sphere_points’) >= min_points
sphere_points = sphere_points joined with sphere_points’
if P’ is not yet member of any cluster
add P’ to cluster C
regionQuery(P, epsilon):
return all points within the n-dimensional sphere centered at P with radius epsilon (including P)
*/
var Point = function Point(v, idx) {
_classCallCheck(this, Point);
this.v = v;
this.idx = idx || 0;
this.k = 0;
this.visited = false;
};
var DBScan = function () {
function DBScan(data, eps, min) {
_classCallCheck(this, DBScan);
this._multi = data[0].length > 0;
this._data = this.initData(data);
this._eps = eps;
this._min = min;
}
_createClass(DBScan, [{
key: "initData",
value: function initData(data) {
var ret = [],
len = data.length;
var multi = this._multi;
for (var i = 0; i < len; i++) {
ret.push(new Point(multi ? data[i] : [data[i]], i));
}
return ret;
}
}, {
key: "regionQuery",
value: function regionQuery(p) {
var eps = this._eps,
data = this._data,
ret = [],
len = data.length;
for (var i = 0; i < len; i++) {
var np = data[i];
if (np != p && np.visited) continue;
if (eudist(np.v, p.v, true) <= eps) ret.push(np);
}
return ret;
}
}, {
key: "expandCluster",
value: function expandCluster(p, region, k) {
var eps = this._eps,
data = this._data,
min = this._min;
// Add p to cluster k
p.k = k.id;
k.data.push(p.v);
// region.length is dynamic becouse items added
// from newRegion to region
for (var j = 0; j < region.length; j++) {
var np = region[j];
if (!np.visited) {
np.visited = true;
var newRegion = this.regionQuery(np),
rlen = newRegion.length;
if (rlen >= min) {
for (var i = 0; i < rlen; i++) {
region.push(newRegion[i]);
}
}
if (!np.k) {
np.k = k.id;
k.data.push(np.v);
}
}
}
}
}, {
key: "dbscan",
value: function dbscan() {
var data = this._data,
min = this._min,
len = data.length,
kid = 0,
ks = [],
// Clusters
noise = [],
// Noise
k = null; // Current cluster
for (var j = 0; j < len; j++) {
var p = data[j];
if (!p.visited) {
// Mark as visited
p.visited = true;
// Get the reachable region for this point
var region = this.regionQuery(p);
// Too small region
if (region.length < min) {
noise.push(p);
} else {
k = { id: kid++, data: [] };
ks.push(k);
this.expandCluster(p, region, k);
}
}
}
// Restore unidimiensional data that was transformed to
// multidimensional for the algoryth purposes
if (!this._multi) {
ks.forEach(function (k) {
k.data = k.data.map(function (v) {
return v[0];
});
});
noise.forEach(function (p) {
return p.v = p.v[0];
});
}
return {
noise: noise.map(function (p) {
return p.v;
}),
clusters: ks
};
}
}]);
return DBScan;
}();
module.exports = function (data, eps, min) {
return new DBScan(data, eps, min).dbscan();
};
}, { "./distance.js": 2 }] }, {}, [1]);
//# sourceMappingURL=sdbscan.js.map