UNPKG

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.

233 lines (194 loc) 8.95 kB
const { describe, it } = require('node:test'); const assert = require('node:assert'); const RatioColumn = require('../../lib/ratio-column/index'); describe('RatioColumn', () => { it('should create a ratio column with valid numeric values', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.deepStrictEqual(column.values, [10, 20, 30, 40, 50]); }); it('should compute correct sum', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.sum, 150); }); it('should compute correct mean', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.mean, 30); }); it('should return correct min and max values', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.min, 10); assert.strictEqual(column.max, 50); }); it('should compute correct range', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.range, 40); }); it('should compute variance and standard deviation (sample)', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.varianceSample.toFixed(2), '250.00'); assert.strictEqual(column.stdDevSample.toFixed(2), '15.81'); }); it('should compute variance and standard deviation (population)', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.variancePopulation.toFixed(2), '200.00'); assert.strictEqual(column.stdDevPopulation.toFixed(2), '14.14'); }); it('should compute correct z-scores', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.deepStrictEqual(column.zScores.map(v => v.toFixed(2)), [ '-1.41', '-0.71', '0.00', '0.71', '1.41' ]); }); it('should return correct quartiles', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.q1, 20); assert.strictEqual(column.q3, 40); assert.strictEqual(column.iqr, 20); }); it('should return correct median', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.median, 30); }); it('should compute mode correctly', () => { const column1 = new RatioColumn([1, 2, 2, 3, 4]); assert.deepStrictEqual(column1.mode, [2]); const column2 = new RatioColumn([1, 2, 3, 4, 5]); assert.deepStrictEqual(column2.mode, []); }); it('should compute geometric mean', () => { const column = new RatioColumn([1, 3, 9, 27, 81]); assert.strictEqual(column.geometricMean.toFixed(2), '9.00'); }); it('should compute harmonic mean', () => { const column = new RatioColumn([1, 2, 3, 4, 5]); assert.strictEqual(column.harmonicMean.toFixed(2), '2.19'); }); it('should compute skewness and kurtosis (population and sample)', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); assert.strictEqual(column.skewnessPopulation.toFixed(2), '1.05'); assert.strictEqual(column.kurtosisPopulation.toFixed(2), '-0.02'); assert.strictEqual(column.skewnessSample.toFixed(2), '1.44'); assert.strictEqual(column.kurtosisSample.toFixed(2), '2.44'); }); it('should compute coefficient of variation (CV)', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); assert.strictEqual(column.cv.toFixed(2), '0.47'); }); it('should detect outliers using z-score', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); const outliers = column.outliersZScore(2); assert.deepStrictEqual(outliers.values, [100]); assert.deepStrictEqual(outliers.indexes, [5]); assert.deepStrictEqual(outliers.zScores.map(v => v.toFixed(2)), ['2.00']); }); it('should detect outliers using IQR method', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); assert.deepStrictEqual(column.outliersIQR, [100]); }); it('should compute confidence interval 95%', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); const ci = column.confidenceInterval95; assert.ok(ci.low < ci.high); assert.ok(ci.width > 0); }); it('should compute weighted mean correctly', () => { const column = new RatioColumn([10, 20, 30, 40, 50]); const weights = [1, 2, 3, 4, 5]; assert.strictEqual(column.weightedMean(weights).toFixed(2), '36.67'); }); it('should compute noise stability', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); assert.ok(column.noiseStability > 0); }); it('should compute spectral power density metrics', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); assert.strictEqual(column.spectralPowerDensityArray.length, column.n); assert.ok(column.spectralPowerDensityMetric > 0); }); it('should correctly normalize values using Min-Max scaling', () => { const column = new RatioColumn([10, 20, 30, 40, 50, 100]); const expectedNormalized = [ (10 - 10) / (100 - 10), (20 - 10) / (100 - 10), (30 - 10) / (100 - 10), (40 - 10) / (100 - 10), (50 - 10) / (100 - 10), (100 - 10) / (100 - 10) ]; assert.deepStrictEqual(column.normalizedValues.map(v => v.toFixed(2)), expectedNormalized.map(v => v.toFixed(2))); }); it('should return an array of zeros if all values are the same', () => { const column = new RatioColumn([5, 5, 5, 5, 5]); assert.deepStrictEqual(column.normalizedValues, [0, 0, 0, 0, 0]); }); it('should return 0 for standard deviation if all values are identical', () => { const column = new RatioColumn([5, 5, 5, 5, 5]); assert.strictEqual(column.stdDevPopulation, 0); }); it('should return NaN for harmonic mean if any value is non-positive', () => { const column = new RatioColumn([5, -10, 15]); assert(Number.isNaN(column.harmonicMean)); }); it('should compute flatness correctly', () => { const column = new RatioColumn([1, 3, 9, 27, 81]); assert.strictEqual(column.flatness.toFixed(2), '0.37'); // geometricMean / mean ≈ 9 / 24.2 }); it('should compute spectral power density metric', () => { const column = new RatioColumn([10, 20, 30]); const spdMetric = column.spectralPowerDensityMetric; assert.ok(spdMetric > 0 && spdMetric < 1); // Ожидаем значение между 0 и 1 }); it('should compute noise stability with single value', () => { const column = new RatioColumn([5]); assert.deepStrictEqual(column.noiseStability, [0]); }); it('should return zeros for normalized values with zero range', () => { const column = new RatioColumn([5, 5, 5]); assert.deepStrictEqual(column.normalizedValues, [0, 0, 0]); }); it('should detect no outliers with IQR', () => { const column = new RatioColumn([1, 2, 3, 4, 5]); assert.deepStrictEqual(column.outliersIQR, []); }); }); describe('xValues', () => { it('xValues returns [1, 2, ..., n]', () => { const col = new RatioColumn([5, 10, 15, 20]); assert.deepStrictEqual(col.xValues, [1, 2, 3, 4]); }); it('xValues is cached (same reference)', () => { const col = new RatioColumn([1, 2, 3]); const first = col.xValues; const second = col.xValues; assert.strictEqual(first, second); }); }); describe('regressionSlope', () => { it('regressionSlope() matches perfect slope with default xValues', () => { const col = new RatioColumn([10, 20, 30, 40]); // perfect slope 10 const slope = col.regressionSlope(); // X: [1,2,3,4] assert.ok(Math.abs(slope - 10) < 1e-6); }); it('regressionSlope() works with customX', () => { const col = new RatioColumn([3, 6, 9]); const slope = col.regressionSlope([1, 2, 3]); assert.ok(Math.abs(slope - 3) < 1e-6); }); it('regressionSlope() returns 0 for constant X', () => { const col = new RatioColumn([10, 20, 30]); const slope = col.regressionSlope([5, 5, 5]); assert.strictEqual(slope, 0); }); it('regressionSlope() throws if customX length mismatch', () => { const col = new RatioColumn([1, 2, 3]); assert.throws(() => { col.regressionSlope([1, 2]); // wrong length }, /same length/i); }); it('regressionSlope() throws if customX is not an array', () => { const col = new RatioColumn([1, 2, 3]); assert.throws(() => { col.regressionSlope("123"); }, /array/i); }); })