Remove other neighbour sampling
This commit is contained in:
@@ -9,7 +9,7 @@ function startNeighbourSamplingSimulation() {
|
|||||||
simulation.stop();
|
simulation.stop();
|
||||||
p1 = performance.now();
|
p1 = performance.now();
|
||||||
|
|
||||||
let force = d3.forceNeighbourSamplingDistance()
|
let force = d3.forceNeighbourSampling()
|
||||||
.neighbourSize(NEIGHBOUR_SIZE)
|
.neighbourSize(NEIGHBOUR_SIZE)
|
||||||
.sampleSize(SAMPLE_SIZE)
|
.sampleSize(SAMPLE_SIZE)
|
||||||
.distance(function (s, t) {
|
.distance(function (s, t) {
|
||||||
|
|||||||
6
index.js
6
index.js
@@ -1,10 +1,4 @@
|
|||||||
/*
|
|
||||||
export {default as forceNeighbourSampling}
|
export {default as forceNeighbourSampling}
|
||||||
from "./src/neighbourSampling";
|
|
||||||
export {default as forceNeighbourSamplingPre}
|
|
||||||
from "./src/neighbourSamplingPre";
|
|
||||||
*/
|
|
||||||
export {default as forceNeighbourSamplingDistance}
|
|
||||||
from "./src/neighbourSamplingDistance";
|
from "./src/neighbourSamplingDistance";
|
||||||
/*
|
/*
|
||||||
export { default as forceBarnesHut}
|
export { default as forceBarnesHut}
|
||||||
|
|||||||
@@ -1,257 +0,0 @@
|
|||||||
import constant from "./constant";
|
|
||||||
import jiggle from "./jiggle";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the node id accessor to the specified i.
|
|
||||||
* @param {node} d - node.
|
|
||||||
* @param {accessor} i - id accessor.
|
|
||||||
* @return {accessor} - node id accessor.
|
|
||||||
*/
|
|
||||||
function index(d, i) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The implementation of Chalmers' 1996 Neighbour and Sampling algorithm.
|
|
||||||
* It uses random sampling to find the most suited neighbours from the
|
|
||||||
* data set.
|
|
||||||
* @return {force} calculated forces.
|
|
||||||
*/
|
|
||||||
export default function () {
|
|
||||||
var id = index,
|
|
||||||
neighbours = [],
|
|
||||||
samples = new Array(),
|
|
||||||
distance = constant(300),
|
|
||||||
nodes,
|
|
||||||
neighbourSize = 6,
|
|
||||||
sampleSize = 3,
|
|
||||||
freeness = 0.85,
|
|
||||||
springForce = 0.7,
|
|
||||||
dampingFactor = 0.3,
|
|
||||||
velocity,
|
|
||||||
multiplier = 50;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the forces at each iteration between the node and the
|
|
||||||
* objects in neighbour and sample sets.
|
|
||||||
* @param {number} alpha - controls the stopping of the
|
|
||||||
* particle simulations.
|
|
||||||
*/
|
|
||||||
function force(alpha) {
|
|
||||||
velocity = 0;
|
|
||||||
for (var i = 0, n = nodes.length; i < n; ++i) {
|
|
||||||
// Randomize the samples for every node.
|
|
||||||
samples[i] = randomizeSample(i);
|
|
||||||
// Calculate the forces between node and its neighbours.
|
|
||||||
for (var [keyN, valueN] of neighbours[i]) {
|
|
||||||
setVelocity(i, keyN, valueN, alpha);
|
|
||||||
}
|
|
||||||
// Calculate the forces between node and its sample set.
|
|
||||||
for (var [keyS, valueS] of samples[i]) {
|
|
||||||
setVelocity(i, keyS, valueS, alpha);
|
|
||||||
}
|
|
||||||
// Check if there are a better neighbours in a sample array
|
|
||||||
// for each node.
|
|
||||||
findNewNeighbours(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the velocities of the source and target nodes.
|
|
||||||
* @param {number} sourceId - source node id.
|
|
||||||
* @param {number} targetId - target node id.
|
|
||||||
* @param {number} dist - high dimensional distance between
|
|
||||||
* the two nodes.
|
|
||||||
* @param {number} alpha - controls the speed of simulation.
|
|
||||||
*/
|
|
||||||
function setVelocity(sourceId, targetId, dist, alpha) {
|
|
||||||
var source, target, x, y, l;
|
|
||||||
source = nodes[sourceId], target = nodes[targetId];
|
|
||||||
// If x or y coordinates not defined, add some randomness.
|
|
||||||
x = target.x + target.vx - source.x - source.vx || jiggle();
|
|
||||||
y = target.y + target.vy - source.y - source.vy || jiggle();
|
|
||||||
l = Math.sqrt(x * x + y * y);
|
|
||||||
l = (l - dist * multiplier) / l * alpha;
|
|
||||||
x *= l, y *= l;
|
|
||||||
velocity += x + y;
|
|
||||||
// Set the calculated velocites for both nodes.
|
|
||||||
target.vx -= x;
|
|
||||||
target.vy -= y;
|
|
||||||
source.vx += x;
|
|
||||||
source.vy += y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the neighbour and sample set at the start.
|
|
||||||
*/
|
|
||||||
function initialize() {
|
|
||||||
if (!nodes) return;
|
|
||||||
|
|
||||||
// Initialize for each node a neighbour and sample arrays
|
|
||||||
// with random values.
|
|
||||||
for (var i = 0, n = nodes.length; i < n; ++i) {
|
|
||||||
var exclude = []; // Array that keeps the indices of nodes to ignore.
|
|
||||||
exclude.push(i);
|
|
||||||
|
|
||||||
var neighbs = createRandomSample(i, exclude, n, neighbourSize);
|
|
||||||
// Sort the neighbour set by the distances.
|
|
||||||
neighbs = new Map([...neighbs.entries()].sort(sortDistances));
|
|
||||||
neighbours[i] = neighbs;
|
|
||||||
|
|
||||||
exclude.concat(neighbs);
|
|
||||||
samples[i] = createRandomSample(i, exclude, n, sampleSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function that compares to map elements by its values.
|
|
||||||
* @param {object} a
|
|
||||||
* @param {object} b
|
|
||||||
* @return {number} - 0, if values are equal, positive number if b > a,
|
|
||||||
* negative otherwise.
|
|
||||||
*/
|
|
||||||
function sortDistances(a, b) {
|
|
||||||
return b[1] - a[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create an array of random integers, all different, with maximum
|
|
||||||
* value max and with size elements. No elements from exlucde should
|
|
||||||
* be included.
|
|
||||||
* @param {number} index - index of current node.
|
|
||||||
* @param {array} exclude - indices of the nodes to ignore.
|
|
||||||
* @param {number} max - maximum value.
|
|
||||||
* @param {number} size - the number of elements in map to return.
|
|
||||||
* @return {map} - a created map that contains random elements from
|
|
||||||
* data set.
|
|
||||||
*/
|
|
||||||
function createRandomSample(index, exclude, max, size) {
|
|
||||||
var randElements = new Map();
|
|
||||||
|
|
||||||
for (var i = 0; i < size; ++i) {
|
|
||||||
// Stop when no new elements can be found.
|
|
||||||
if (randElements.size + exclude.length >= nodes.length) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
var rand = Math.floor((Math.random() * max));
|
|
||||||
// If the rand is already in random list or in exclude list
|
|
||||||
// ignore it and get a new value.
|
|
||||||
while (randElements.has(rand) || exclude.includes(rand)) {
|
|
||||||
rand = Math.floor((Math.random() * max));
|
|
||||||
}
|
|
||||||
randElements.set(rand, +distance(nodes[index], nodes[rand]));
|
|
||||||
}
|
|
||||||
|
|
||||||
return randElements;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new map of random numbers to be used by the samples list.
|
|
||||||
* @param {number} index - index of current node.
|
|
||||||
* @return {map} - map that contains random elements from data set.
|
|
||||||
*/
|
|
||||||
function randomizeSample(index) {
|
|
||||||
// Ignore the current neighbours of the node and itself.
|
|
||||||
var exclude = [index];
|
|
||||||
exclude = exclude.concat(Array.from(neighbours[index].keys()));
|
|
||||||
return createRandomSample(index, exclude, nodes.length, sampleSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compares the elements from sample set to the neighbour set and
|
|
||||||
* replaces the elements from neighbour set if better neighbours are
|
|
||||||
* found in sample set.
|
|
||||||
* @param {number} index - index of current node.
|
|
||||||
*/
|
|
||||||
function findNewNeighbours(index) {
|
|
||||||
var sample = samples[index];
|
|
||||||
|
|
||||||
for (var [key, value] of sample) {
|
|
||||||
var neighbMax = neighbours[index].entries().next().value;
|
|
||||||
|
|
||||||
// Check if a value from sample could be a better neighbour
|
|
||||||
// if so, replace it.
|
|
||||||
if (value < neighbMax[1]) {
|
|
||||||
neighbours[index].delete(neighbMax[0]);
|
|
||||||
neighbours[index].set(key, value)
|
|
||||||
neighbours[index] = new Map([...neighbours[index].entries()].sort(sortDistances));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the stress. Basically, it computes the difference between
|
|
||||||
* high dimensional distance and real distance. The lower the stress is,
|
|
||||||
* the better layout.
|
|
||||||
* @return {number} - stress of the layout.
|
|
||||||
*/
|
|
||||||
function getStress() {
|
|
||||||
var totalDiffSq = 0, totalHighDistSq = 0;
|
|
||||||
for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) {
|
|
||||||
for (var j = 0; j < nodes.length; j++) {
|
|
||||||
if (i !== j) {
|
|
||||||
source = nodes[i], target = nodes[j];
|
|
||||||
realDist = Math.hypot(target.x - source.x, target.y - source.y);
|
|
||||||
highDist = +distance(source, target);
|
|
||||||
totalDiffSq += Math.pow(realDist - highDist, 2);
|
|
||||||
totalHighDistSq += highDist * highDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Math.sqrt(totalDiffSq / totalHighDistSq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the average velocity of the force calculation at the
|
|
||||||
* current iteration.
|
|
||||||
* @return {number} - average velocity.
|
|
||||||
*/
|
|
||||||
function getAvgVelocity() {
|
|
||||||
return velocity / ((neighbourSize + sampleSize) * nodes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// API for initializing the algorithm, setting parameters and querying
|
|
||||||
// metrics.
|
|
||||||
force.initialize = function (_) {
|
|
||||||
nodes = _;
|
|
||||||
initialize();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.id = function (_) {
|
|
||||||
return arguments.length ? (id = _, force) : id;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.neighbourSize = function (_) {
|
|
||||||
return arguments.length ? (neighbourSize = +_, force) : neighbourSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.sampleSize = function (_) {
|
|
||||||
return arguments.length ? (sampleSize = +_, force) : sampleSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.distance = function (_) {
|
|
||||||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.stress = function () {
|
|
||||||
return getStress();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.velocity = function () {
|
|
||||||
return getAvgVelocity();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.freeness = function (_) {
|
|
||||||
return arguments.length ? (freeness = +_, force) : freeness;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.nodeNeighbours = function (_) {
|
|
||||||
return arguments.length ? neighbours[+_] : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
force.multiplier = function (_) {
|
|
||||||
return arguments.length ? (multiplier = +_, force) : multiplier;
|
|
||||||
};
|
|
||||||
|
|
||||||
return force;
|
|
||||||
}
|
|
||||||
@@ -1,197 +0,0 @@
|
|||||||
import constant from "./constant";
|
|
||||||
import jiggle from "./jiggle";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the node id accessor to the specified i.
|
|
||||||
* @param {node} d - node.
|
|
||||||
* @param {accessor} i - id accessor.
|
|
||||||
* @return {accessor} - node id accessor.
|
|
||||||
*/
|
|
||||||
function index(d, i) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The implementation of Chalmers' 1996 Neighbour and Sampling algorithm.
|
|
||||||
* It uses random sampling to find the most suited neighbours from the
|
|
||||||
* data set.
|
|
||||||
* @return {force} calculated forces.
|
|
||||||
*/
|
|
||||||
export default function () {
|
|
||||||
var id = index,
|
|
||||||
neighbours = [],
|
|
||||||
worst = [],
|
|
||||||
samples = new Array(),
|
|
||||||
distance = constant(300),
|
|
||||||
nodes,
|
|
||||||
neighbourSize = 6,
|
|
||||||
sampleSize = 3,
|
|
||||||
freeness = 0.85,
|
|
||||||
springForce = 0.7,
|
|
||||||
dampingFactor = 0.3;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the forces at each iteration between the node and the
|
|
||||||
* objects in neighbour and sample sets.
|
|
||||||
* @param {number} alpha - controls the stopping of the
|
|
||||||
* particle simulations.
|
|
||||||
*/
|
|
||||||
function force(alpha) {
|
|
||||||
for (var i = 0, n = nodes.length; i < n; ++i) {
|
|
||||||
// Randomize the samples for every node.
|
|
||||||
// Calculate the forces between node and its neighbours.
|
|
||||||
for (var [keyN, valueN] of neighbours[i]) {
|
|
||||||
setVelocity(i, keyN, valueN, alpha);
|
|
||||||
}
|
|
||||||
// Calculate the forces between node and its sample set.
|
|
||||||
// for (var [keyS, valueS] of worst[i]) {
|
|
||||||
// setVelocity(i, keyS, valueS, alpha);
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the velocities of the source and target nodes.
|
|
||||||
* @param {number} sourceId - source node id.
|
|
||||||
* @param {number} targetId - target node id.
|
|
||||||
* @param {number} dist - high dimensional distance between
|
|
||||||
* the two nodes.
|
|
||||||
* @param {number} alpha - controls the speed of simulation.
|
|
||||||
*/
|
|
||||||
function setVelocity(sourceId, targetId, dist, alpha) {
|
|
||||||
var source, target, x, y, l;
|
|
||||||
source = nodes[sourceId], target = nodes[targetId];
|
|
||||||
// If x or y coordinates not defined, add some randomness.
|
|
||||||
x = target.x + target.vx - source.x - source.vx || jiggle();
|
|
||||||
y = target.y + target.vy - source.y - source.vy || jiggle();
|
|
||||||
l = Math.sqrt(x * x + y * y);
|
|
||||||
l = (l - dist) / l * alpha;
|
|
||||||
x *= l, y *= l;
|
|
||||||
// Set the calculated velocites for both nodes.
|
|
||||||
target.vx -= x;
|
|
||||||
target.vy -= y;
|
|
||||||
source.vx += x;
|
|
||||||
source.vy += y;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the neighbour and sample set at the start.
|
|
||||||
*/
|
|
||||||
function initialize() {
|
|
||||||
if (!nodes) return;
|
|
||||||
|
|
||||||
findNeighbours();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function that compares to map elements by its values.
|
|
||||||
* @param {object} a
|
|
||||||
* @param {object} b
|
|
||||||
* @return {number} - 0, if values are equal, positive number if b > a,
|
|
||||||
* negative otherwise.
|
|
||||||
*/
|
|
||||||
function sortDistances(a, b) {
|
|
||||||
return b[1] - a[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function findNeighbours() {
|
|
||||||
// Initialize for each node a neighbour and sample arrays
|
|
||||||
// with random values.
|
|
||||||
for (var i = 0, n = nodes.length; i < n; ++i) {
|
|
||||||
neighbours[i] = new Map();
|
|
||||||
for (var j = 0; j < n; j++) {
|
|
||||||
if (i !== j) {
|
|
||||||
var dist = +distance(nodes[i], nodes[j]);
|
|
||||||
|
|
||||||
if (neighbours[i].size < neighbourSize) {
|
|
||||||
neighbours[i].set(j, dist);
|
|
||||||
neighbours[i] = new Map([...neighbours[i].entries()].sort(sortDistances));
|
|
||||||
} else {
|
|
||||||
var neighbMax = neighbours[i].entries().next().value;
|
|
||||||
|
|
||||||
if (dist < neighbMax[1]) {
|
|
||||||
neighbours[i].delete(neighbMax[0]);
|
|
||||||
neighbours[i].set(j, dist);
|
|
||||||
neighbours[i] = new Map([...neighbours[i].entries()].sort(sortDistances));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the stress. Basically, it computes the difference between
|
|
||||||
* high dimensional distance and real distance. The lower the stress is,
|
|
||||||
* the better layout.
|
|
||||||
* @return {number} - stress of the layout.
|
|
||||||
*/
|
|
||||||
function getStress() {
|
|
||||||
var totalDiffSq = 0, totalHighDistSq = 0;
|
|
||||||
for (var i = 0, source, target, realDist, highDist; i < nodes.length; i++) {
|
|
||||||
for (var j = 0; j < nodes.length; j++) {
|
|
||||||
if (i !== j) {
|
|
||||||
source = nodes[i], target = nodes[j];
|
|
||||||
realDist = Math.hypot(target.x - source.x, target.y - source.y);
|
|
||||||
highDist = +distance(source, target);
|
|
||||||
totalDiffSq += Math.pow(realDist - highDist, 2);
|
|
||||||
totalHighDistSq += highDist * highDist;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Math.sqrt(totalDiffSq / totalHighDistSq);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the average velocity of the force calculation at the
|
|
||||||
* current iteration.
|
|
||||||
* @return {number} - average velocity.
|
|
||||||
*/
|
|
||||||
function getAvgVelocity() {
|
|
||||||
return velocity / ((neighbourSize + sampleSize) * nodes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
// API for initializing the algorithm, setting parameters and querying
|
|
||||||
// metrics.
|
|
||||||
force.initialize = function (_) {
|
|
||||||
nodes = _;
|
|
||||||
initialize();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.id = function (_) {
|
|
||||||
return arguments.length ? (id = _, force) : id;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.neighbourSize = function (_) {
|
|
||||||
return arguments.length ? (neighbourSize = +_, force) : neighbourSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.sampleSize = function (_) {
|
|
||||||
return arguments.length ? (sampleSize = +_, force) : sampleSize;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.distance = function (_) {
|
|
||||||
return arguments.length ? (distance = typeof _ === "function" ? _ : constant(+_), force) : distance;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.stress = function () {
|
|
||||||
return getStress();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.velocity = function () {
|
|
||||||
return getAvgVelocity();
|
|
||||||
};
|
|
||||||
|
|
||||||
force.freeness = function (_) {
|
|
||||||
return arguments.length ? (freeness = +_, force) : freeness;
|
|
||||||
};
|
|
||||||
|
|
||||||
force.nodeNeighbours = function (_) {
|
|
||||||
return arguments.length ? neighbours[+_] : [];
|
|
||||||
};
|
|
||||||
|
|
||||||
return force;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user