als-statistics
Version:
A powerful and lightweight JavaScript library for descriptive statistics, regression, clustering, outlier detection, and noise analysis using a flexible table/column architecture.
222 lines (186 loc) • 8.38 kB
JavaScript
const { describe, it, before } = require('node:test');
const assert = require('node:assert');
const Table = require('../../lib/table/index');
const RatioColumn = require('../../lib/ratio-column/index');
const Column = require('../../lib/column/index');
describe('Table class', () => {
it('should create a table', () => {
const table = new Table();
assert.deepStrictEqual(table.columns, {});
assert.strictEqual(table.n, 0);
});
it('should add columns', () => {
const table = new Table();
const ratio = table.addColumn('Numbers', [10, 20, 30]);
const strCol = table.addColumn('Category', ['A', 'B', 'C']);
assert(ratio instanceof RatioColumn);
assert(strCol instanceof Column);
});
it('should filter and clear filters', () => {
const table = new Table();
table.addColumn('Numbers', [10, 20, 30, 40, 50]);
table.filterRows([2, 3]);
assert.deepStrictEqual(table.filtered, { 2: true, 3: true });
table.clearAllRowsFilters();
assert.deepStrictEqual(table.filtered, {});
});
it('should compare two ratio columns', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3, 4, 5]);
table.addColumn('B', [2, 3, 4, 5, 6]);
const comparison = table.compare('A', 'B');
assert.strictEqual(comparison.covariancePopulation.toFixed(2), '2.00');
});
it('should compute a new column based on existing columns', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3]);
table.addColumn('B', [4, 5, 6]);
const newColumn = table.compute(({ A, B }) => A + B, 'Sum');
assert.deepStrictEqual(newColumn.values, [5, 7, 9]);
assert(table.columns['Sum'] instanceof RatioColumn);
});
it('should transpose the table', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3]);
table.addColumn('B', [4, 5, 6]);
const transposed = table.transpose();
assert.strictEqual(transposed.n, 2);
assert.strictEqual(Object.keys(transposed.columns).length, 3);
});
it('should calculate descriptive statistics', () => {
const table = new Table();
table.addColumn('Numbers', [10, 20, 30, 40, 50]);
const desc = table.descriptive('mean');
assert.strictEqual(desc.values[0], 30);
});
it('should delete a column', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3]);
table.addColumn('B', [4, 5, 6]);
table.deleteColumn('A');
assert.strictEqual(Object.keys(table.columns).length, 1);
assert.strictEqual(table.columns['A'], undefined);
assert.strictEqual(table.n, 3); // n не уменьшается, что может быть нежелательно
});
it('should clone table with filtered values', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3, 4, 5]);
table.filterRows([1, 2, 3]);
const cloned = table.clone(true);
assert.deepStrictEqual(cloned.columns['A'].values, [1, 5]);
assert.strictEqual(cloned.n, 2);
});
it('should clone table with original values', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3, 4, 5]);
table.filterRows([1, 2, 3]);
const cloned = table.clone(false);
assert.deepStrictEqual(cloned.columns['A'].values, [1, 2, 3, 4, 5]);
assert.strictEqual(cloned.n, 5);
});
it('should transpose with filtered rows', () => {
const table = new Table();
table.addColumn('A', [1, 2, 3]);
table.filterRows([1]);
const transposed = table.transpose();
assert.strictEqual(Object.keys(transposed.columns).length, 2); // 0 и 2
});
});
describe('Cloning with filter columns', () => {
let table
before(() => {
table = new Table();
table.addColumn("A", [1, 2, 3]);
table.addColumn("B", [4, 5, 6]);
table.addColumn("C", [7, 8, 9]);
})
it('should clone table with explicit include', () => {
const cloned = table.clone(true, ["A", "C"]);
assert.deepStrictEqual(cloned.columns["A"].values, [1, 2, 3]);
assert.deepStrictEqual(cloned.columns["C"].values, [7, 8, 9]);
assert.strictEqual(cloned.columns["B"], undefined);
});
it('should clone table with exclude only', () => {
const cloned = table.clone(true, ["-B"]);
assert.deepStrictEqual(cloned.columns["A"].values, [1, 2, 3]);
assert.deepStrictEqual(cloned.columns["C"].values, [7, 8, 9]);
assert.strictEqual(cloned.columns["B"], undefined);
});
it('should clone table with regex and exclude', () => {
table.addColumn("A1", [10, 11, 12]);
const cloned = table.clone(true, [/^A/, "-A1"]);
assert.deepStrictEqual(cloned.columns["A"].values, [1, 2, 3]);
assert.strictEqual(cloned.columns["A1"], undefined);
assert.strictEqual(cloned.columns["B"], undefined);
});
it('should ignore exclude with include', () => {
const cloned = table.clone(true, ["A", "-B"]);
assert.deepStrictEqual(cloned.columns["A"].values, [1, 2, 3]);
assert.strictEqual(cloned.columns["B"], undefined);
assert.strictEqual(cloned.columns["C"], undefined);
});
})
describe('Table class - New Features', () => {
it('should create table with initial data using constructor', () => {
const data = {
Numbers: [10, 20, 30],
Letters: ['A', 'B', 'C']
};
const table = new Table(data);
assert.strictEqual(table.n, 3);
assert.strictEqual(table.columnsN, 2);
assert.ok(table.columns['Numbers'] instanceof RatioColumn);
assert.ok(table.columns['Letters'] instanceof Column);
assert.deepStrictEqual(table.columns['Numbers'].values, [10, 20, 30]);
assert.deepStrictEqual(table.columns['Letters'].values, ['A', 'B', 'C']);
});
it('should add row at the end when index is null', () => {
const table = new Table();
table.addColumn('Numbers', [1, 2]);
table.addColumn('Letters', ['A', 'B']);
const result = table.addRow({ Numbers: 3, Letters: 'C' });
assert.strictEqual(result, true);
assert.strictEqual(table.n, 3);
assert.deepStrictEqual(table.columns['Numbers'].values, [1, 2, 3]);
assert.deepStrictEqual(table.columns['Letters'].values, ['A', 'B', 'C']);
});
it('should add row at specific index', () => {
const table = new Table();
table.addColumn('Numbers', [1, 3]);
table.addColumn('Letters', ['A', 'C']);
const result = table.addRow({ Numbers: 2, Letters: 'B' }, 1);
assert.strictEqual(result, true);
assert.strictEqual(table.n, 3);
assert.deepStrictEqual(table.columns['Numbers'].values, [1, 2, 3]);
assert.deepStrictEqual(table.columns['Letters'].values, ['A', 'B', 'C']);
});
it('should return false for invalid row data', () => {
const table = new Table();
table.addColumn('Numbers', [1, 2]);
table.addColumn('Letters', ['A', 'B']);
// Неправильный тип
let result = table.addRow({ Numbers: '3', Letters: 'C' });
assert.strictEqual(result, false);
// Отсутствует ключ
result = table.addRow({ Numbers: 3 });
assert.strictEqual(result, false);
// Лишний ключ
result = table.addRow({ Numbers: 3, Letters: 'C', Extra: 1 });
assert.strictEqual(result, false);
});
it('should filter indexes with where method', () => {
const table = new Table();
table.addColumn('Numbers', [10, 20, 30, 40]);
table.addColumn('Letters', ['A', 'B', 'C', 'D']);
const result = table.where((row) => row.Numbers < 25);
assert.deepStrictEqual(result, [0, 1]);
});
it('should respect existing filters in where method', () => {
const table = new Table();
table.addColumn('Numbers', [10, 20, 30, 40]);
table.addColumn('Letters', ['A', 'B', 'C', 'D']);
table.filterRows([1]); // Фильтруем строку с 20
const result = table.where((row) => row.Numbers > 15);
assert.deepStrictEqual(result, [2, 3]);
});
});