als-statistics
Version:
Modular JS statistics toolkit for Node.js and the browser: descriptive stats, correlations (Pearson/Spearman/Kendall), t-tests & ANOVA (Student/Welch), reliability (Cronbach’s alpha), regression (linear/logistic), clustering (DBSCAN/HDBSCAN), and table/co
50 lines (44 loc) • 1.84 kB
JavaScript
import { Pearson } from '../correlate/pearson.js'
function meanAbsDiff(x, y) {
const n = Math.min(x.length, y.length);
if (n === 0) return NaN; // пусть валидация поймает
let s = 0;
for (let i = 0; i < n; i++) s += Math.abs(x[i] - y[i]);
return s / n;
}
function rangeUnionPlus(x, y) {
let minv = Infinity, maxv = -Infinity;
for (let i = 0; i < x.length; i++) { const v = x[i]; if (v < minv) minv = v; if (v > maxv) maxv = v; }
for (let i = 0; i < y.length; i++) { const v = y[i]; if (v < minv) minv = v; if (v > maxv) maxv = v; }
const r = maxv - minv;
return (r === 0 || !Number.isFinite(r)) ? 1 : (r + 1);
}
function pearson(x, y) {
const { r } = new Pearson({ x: x.values, y: y.values }, true)
return Number.isFinite(r) ? Math.max(-1, Math.min(1, r)) : NaN
}
export default function computeDistances(samples, metric = 'mad') {
const k = samples.length
const distances = Array.from({ length: k }, () => Array(k).fill(0));
for (let i = 0; i < k; i++) {
const colI = samples[i];
for (let j = i + 1; j < k; j++) {
const colJ = samples[j];
let dist;
if (metric === 'pearson') {
const r = pearson(colI, colJ);
if (!Number.isFinite(r)) throw new Error('Pearson correlation failed: non-finite');
dist = 1 - r;
} else { // 'mad'
const mad = meanAbsDiff(colI.values, colJ.values);
const rng = rangeUnionPlus(colI.values, colJ.values);
if (!Number.isFinite(mad) || !Number.isFinite(rng)) throw new Error('MAD distance failed: non-finite');
dist = mad / rng;
}
distances[i][j] = dist;
distances[j][i] = dist;
}
distances[i][i] = 0;
}
return distances;
}