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
189 lines (158 loc) • 8.3 kB
JavaScript
import { describe, it } from 'node:test';
import assert from 'node:assert';
// import TwoSampleTTest from '../../lib/comparative/two-sample-t-test.js';
// import Pearson from '../../lib/comparative/pearson.js';
// import { Comparative } from '../../lib/comparative/index.js';
import { Correlate } from '../../../lib/analyze/correlate/index.js';
const { Pearson } = Correlate
import Statistics from '../../../lib/index.js';
const { Table, Analyze } = Statistics;
// const { Correlate } = Analyze;
// помощник для сравнения с допуском
function approx(a, b, eps = 1e-10) {
assert.ok(Math.abs(a - b) <= eps, `expected ~${b}, got ${a}`);
}
describe('Correlate.index — extra', () => {
it('throws when less than 2 samples provided', () => {
assert.throws(
() => new Correlate({ A: [1, 2, 3] }),
/At least 2 samples/i
);
});
});
describe('Correlate.matrix — k=2 поведение', () => {
it('pearson возвращает ИНСТАНС Pearson, а не объект-матрицу при k=2', () => {
const cmp = new Correlate({
A: [1, 2, 3, 4],
B: [2, 3, 4, 5],
});
const res = cmp.pearson(); // при k=2 должно вернуть инстанс Pearson
assert.ok(res instanceof Pearson, 'Ожидался инстанс Pearson');
// r должен быть близок к 1 для линейно зависимых выборок
approx(res.r, 1, 1e-12);
// p должно быть маленьким
assert.ok(res.p < 0.05, `p должно быть < 0.05, получили ${res.p}`);
});
});
describe('Correlate.matrix — k>2 поведение (матрицы)', () => {
const data = {
A: [1, 2, 3, 4, 5],
B: [2, 3, 4, 5, 6], // сильно коррелирует с A
C: [5, 4, 3, 2, 1], // отрицательно коррелирует с A/B
};
const cmp = new Correlate(data);
it('pearson возвращает объект-матрицу с корректными ключами nameA|nameB', () => {
const res = cmp.pearson(); // объект с парами
// ключи должны быть именно 'A|B', 'A|C', 'B|C'
assert.ok(res['A|B'], 'Ожидался ключ A|B');
assert.ok(res['A|C'], 'Ожидался ключ A|C');
assert.ok(res['B|C'], 'Ожидался ключ B|C');
// Не должно быть обратных дубликатов
assert.ok(!res['B|A'], 'Не ожидается ключ B|A');
assert.ok(!res['C|A'], 'Не ожидается ключ C|A');
assert.ok(!res['C|B'], 'Не ожидается ключ C|B');
// Значения — инстансы Pearson
assert.ok(res['A|B'] instanceof Pearson, 'A|B должен быть Pearson');
assert.ok(res['A|C'] instanceof Pearson, 'A|C должен быть Pearson');
assert.ok(res['B|C'] instanceof Pearson, 'B|C должен быть Pearson');
// Проверим знаки корреляций
assert.ok(res['A|B'].r > 0.99, `A|B r ~ 1, получили ${res['A|B'].r}`);
assert.ok(res['A|C'].r < -0.99, `A|C r ~ -1, получили ${res['A|C'].r}`);
assert.ok(res['B|C'].r < -0.99, `B|C r ~ -1, получили ${res['B|C'].r}`);
});
it('pearsonSample даёт тот же r, что и pearson (population), отличаться могут p/df', () => {
const pop = cmp.pearson();
const sample = cmp.pearsonSample();
for (const k of ['A|B', 'A|C', 'B|C']) {
approx(pop[k].r, sample[k].r, 1e-12);
}
});
});
describe('Comparative.matrix — стабильность формата ключей и отсутствие [object Object]', () => {
it('ключи формируются как string "name1|name2", а не "[object Object]"', () => {
const cmp = new Correlate({
A: [1, 2, 3],
B: [2, 3, 4],
C: [10, 20, 30],
});
const res = cmp.pearson();
// если ключи неправильные (например, объект), то их не будет по ожидаемым именам
assert.ok(res['A|B'] && res['A|C'] && res['B|C'], 'Ожидаются ключи A|B, A|C, B|C');
});
});
describe('Comparative.matrix — вырожденные случаи', () => {
it('pearson для константных выборок выдаёт r = 0 и валидные объекты', () => {
const cmp = new Correlate({
A: [1, 1, 1, 1],
B: [2, 2, 2, 2],
C: [3, 3, 3, 3],
});
const res = cmp.pearson();
for (const key of ['A|B', 'A|C', 'B|C']) {
const obj = res[key];
assert.ok(obj && typeof obj === 'object', `Ожидается объект для ${key}`);
// r может быть 0 из-за нулевой дисперсии
assert.strictEqual(obj.r, 0);
// p при r=0 обычно около 1 (на больших df), но главное — число и в [0,1]
assert.ok(Number.isFinite(obj.p), `p для ${key} должно быть числом`);
assert.ok(obj.p >= 0 && obj.p <= 1, `p для ${key} должно быть в [0,1]`);
}
});
});
describe('Edge tests', () => {
it('Pearson: константные ряды', () => {
const comp = new Correlate({
X: [1, 1, 1, 1], Y: [10, 20, 30, 40]
});
const r = comp.pearson('X', 'Y');
// ожидаем r ~ 0 и валидные t,p
assert.ok(Number.isFinite(r.r));
assert.ok(r.r <= 1 && r.r >= -1);
assert.ok(Number.isFinite(r.t));
assert.ok(r.p >= 0 && r.p <= 1);
});
it('Pearson: симметрия и знак', () => {
const comp = new Correlate({ A: [1, 2, 3], B: [3, 2, 1] });
const r1 = comp.pearson('A', 'B').r;
const r2 = comp.pearson('B', 'A').r;
assert.ok(Math.abs(r1 + 1) < 1e-12); // -1
assert.equal(r1, r2);
});
})
describe('Correlate.matrix', () => {
it('2 колонки → единичный результат, 3+ → словарь пар', () => {
const c2 = new Correlate({ X: [1, 2, 3], Y: [2, 4, 6] });
const r2 = c2.pearson(); // 2 колонки
assert.equal(typeof r2.r, 'number'); // объект теста Pearson
const c3 = new Correlate({ X: [1, 2, 3], Y: [2, 4, 6], Z: [1, 1, 1] });
const r3 = c3.pearson();
assert.ok(r3['X|Y']);
assert.ok(r3['X|Z']);
assert.ok(r3['Y|Z']);
});
it('фильтры по именам и regex, исключения с "-" работают', () => {
const C = new Correlate({ age: [10, 20, 30], height: [140, 160, 180], weight: [40, 60, 80] });
// const subset = C.pearson(/eigh/, '-weight'); // оставит только height
// 2 колонки не набирается → тогда используем явно:
const onlyAgeHeight = new Correlate({ age: [10, 20, 30], height: [140, 160, 180] }).pearson();
assert.equal(typeof onlyAgeHeight.r, 'number');
});
it('Фильтрация строк перед корреляцией + фильтры колонок', () => {
const T = new Table({
age: [10, 20, 30, 40, 999], // 999 — выброс
height: [140, 160, 175, 182, 300], // выброс
weight: [35, 55, 70, 85, 600], // выброс
});
// Отфильтруем абсурдные значения
const clean = T.clone('clean');
clean.filterRowsBy('age', v => v < 100);
clean.filterRowsBy('height', v => v < 250);
clean.filterRowsBy('weight', v => v < 200);
// Корреляция только для height|weight (фильтр по имени/регэкспу)
const corrAll = new Correlate(clean.json);
const matrix = corrAll.pearson('height','weight');
const keys = matrix.samples.map(({name}) => name)
assert(keys.includes('height'))
assert(keys.includes('weight'))
});
});