@jbrowse/core
Version:
JBrowse 2 core libraries used by plugins
97 lines (96 loc) • 3.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.euclideanDistance = euclideanDistance;
exports.averageDistance = averageDistance;
exports.clusterData = clusterData;
const stopToken_1 = require("@jbrowse/core/util/stopToken");
function toP(n) {
return Number.parseFloat((n * 100).toFixed(1));
}
function euclideanDistance(a, b) {
const size = Math.min(a.length, b.length);
let sum = 0;
for (let index = 0; index < size; index++) {
sum += (a[index] - b[index]) * (a[index] - b[index]);
}
return Math.sqrt(sum);
}
function averageDistance(setA, setB, distances) {
let distance = 0;
const lenA = setA.length;
const lenB = setB.length;
for (let i = 0; i < lenA; i++) {
for (let j = 0; j < lenB; j++) {
distance += distances[setA[i]][setB[j]];
}
}
return distance / setA.length / setB.length;
}
function clusterData({ data, distance = euclideanDistance, linkage = averageDistance, onProgress, stopToken, }) {
let start = performance.now();
const distances = [];
for (let i = 0; i < data.length; i++) {
if (performance.now() - start > 400) {
(0, stopToken_1.checkStopToken)(stopToken);
start = performance.now();
}
if (onProgress) {
onProgress(`Making distance matrix: ${toP(i / (data.length - 1))}%`);
}
const row = [];
for (let j = 0; j < data.length; j++) {
row.push(distance(data[i], data[j]));
}
distances.push(row);
}
const clusters = data.map((_datum, index) => ({
height: 0,
indexes: [Number(index)],
}));
let clustersGivenK = [];
start = performance.now();
for (let iteration = 0; iteration < data.length; iteration++) {
if (performance.now() - start > 400) {
(0, stopToken_1.checkStopToken)(stopToken);
start = performance.now();
}
if (onProgress) {
onProgress(`Clustering: ${toP((iteration + 1) / data.length)}%`);
}
clustersGivenK.push(clusters.map(cluster => cluster.indexes));
if (iteration >= data.length - 1) {
break;
}
let nearestDistance = Infinity;
let nearestRow = 0;
let nearestCol = 0;
for (let row = 0; row < clusters.length; row++) {
for (let col = row + 1; col < clusters.length; col++) {
const distance = linkage(clusters[row].indexes, clusters[col].indexes, distances);
if (distance < nearestDistance) {
nearestDistance = distance;
nearestRow = row;
nearestCol = col;
}
}
}
const newCluster = {
indexes: [
...clusters[nearestRow].indexes,
...clusters[nearestCol].indexes,
],
height: nearestDistance,
children: [clusters[nearestRow], clusters[nearestCol]],
};
clusters.splice(Math.max(nearestRow, nearestCol), 1);
clusters.splice(Math.min(nearestRow, nearestCol), 1);
clusters.push(newCluster);
}
clustersGivenK = [[], ...clustersGivenK.reverse()];
return {
clusters: clusters[0],
distances: distances,
order: clusters[0].indexes,
clustersGivenK: clustersGivenK,
};
}