File size: 1,897 Bytes
bc20498 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 |
// Common distance metrics for clustering algorithms
// https://en.wikipedia.org/wiki/Hierarchical_clustering#Metric
import * as is from '../../is';
let identity = x => x;
let absDiff = ( p, q ) => Math.abs( q - p );
let addAbsDiff = ( total, p, q ) => total + absDiff(p, q);
let addSquaredDiff = ( total, p, q ) => total + Math.pow( q - p, 2 );
let sqrt = x => Math.sqrt(x);
let maxAbsDiff = ( currentMax, p, q ) => Math.max( currentMax, absDiff(p, q) );
let getDistance = function( length, getP, getQ, init, visit, post = identity ){
let ret = init;
let p, q;
for ( let dim = 0; dim < length; dim++ ) {
p = getP(dim);
q = getQ(dim);
ret = visit( ret, p, q );
}
return post( ret );
};
let distances = {
euclidean: function ( length, getP, getQ ) {
if( length >= 2 ){
return getDistance( length, getP, getQ, 0, addSquaredDiff, sqrt );
} else { // for single attr case, more efficient to avoid sqrt
return getDistance( length, getP, getQ, 0, addAbsDiff );
}
},
squaredEuclidean: function ( length, getP, getQ ) {
return getDistance( length, getP, getQ, 0, addSquaredDiff );
},
manhattan: function ( length, getP, getQ ) {
return getDistance( length, getP, getQ, 0, addAbsDiff );
},
max: function ( length, getP, getQ ) {
return getDistance( length, getP, getQ, -Infinity, maxAbsDiff );
}
};
// in case the user accidentally doesn't use camel case
distances['squared-euclidean'] = distances['squaredEuclidean'];
distances['squaredeuclidean'] = distances['squaredEuclidean'];
export default function( method, length, getP, getQ, nodeP, nodeQ ){
let impl;
if( is.fn( method ) ){
impl = method;
} else {
impl = distances[ method ] || distances.euclidean;
}
if( length === 0 && is.fn( method ) ){
return impl( nodeP, nodeQ );
} else {
return impl( length, getP, getQ, nodeP, nodeQ );
}
}
|