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

55 lines (47 loc) 2.46 kB
import CDF from '../cdf/index.js'; import { TestBase } from '../test-base/index.js' export class OneWayAnova extends TestBase { /** One-way ANOVA for prepared samples object: { name1: number[], name2: number[], ... } */ constructor(samples, welch = false) { super(samples, 'One-way ANOVA', ['F', 'dfBetween', 'dfWithin', 'p', 'k', 'msw']) this.#calc(welch) } #calc(welch) { this.dfBetween = this.k - 1; if (welch) { // TODO not tests const means = [], ns = [], vars = [], weights = []; this.samples.forEach(({ mean, n, varianceSample }) => { means.push(mean); ns.push(n); vars.push(varianceSample); weights.push(n / varianceSample); // Веса Вэлча: w_i = n_i / s_i^2 }); // Сумма весов и взвешенная общая средняя const W = weights.reduce((a, b) => a + b, 0); const gmW = means.reduce((s, mi, i) => s + weights[i] * mi, 0) / W; // Межгрупповая сумма квадратов по Вэлчу const ssbW = means.reduce((s, mi, i) => s + weights[i] * (mi - gmW) ** 2, 0); const msbW = ssbW / (this.k - 1); // Коррекция знаменателя и степеней свободы (Welch–Satterthwaite) const a_i = weights.map(wi => 1 - wi / W); const sum_ai = ns.reduce((s, ni, i) => s + (a_i[i] * a_i[i]) / (ni - 1), 0); const c = 1 + (2 * (this.k - 2) / (this.k * this.k - 1)) * sum_ai; const df2 = (this.k * this.k - 1) / (3 * sum_ai); this.ssb = ssbW; this.msb = msbW; this.dfWithin = df2; this.ssw = c * df2; // => msw = c, F = msbW / c — как у Welch } else { const N = this.samples.reduce((t, { n }) => t + n, 0); this.dfWithin = N - this.k; this.grandMean = this.samples.reduce((t, { sum }) => t + sum, 0) / N this.ssb = this.samples.reduce((t, { mean, n }) => t + n * Math.pow(mean - this.grandMean, 2), 0); this.ssw = this.samples.reduce((t, { mean, values }) => t + values.reduce((acc, v) => acc + (v - mean) ** 2, 0), 0); this.msb = this.ssb / this.dfBetween } this.msw = this.ssw / this.dfWithin this.F = this.msb / this.msw } get p() { if (this._p === undefined) this._p = 1 - CDF.f(this.F, this.dfBetween, this.dfWithin); return this._p; } }