UNPKG

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
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')) }); });