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
61 lines (50 loc) • 2.38 kB
JavaScript
import CDF from '../cdf/index.js';
import { TestBase } from '../test-base/index.js'
export class IndependentTTest extends TestBase { /** Independent-samples t-test (Student/Welch) + Levene’s test (как в SPSS) */
constructor(samples, welch = false) {
super(samples, 'Independent T Test', [ 't','df','p','F', 'leveneF','leveneDf1','leveneDf2','leveneP', 'k']);
this.#calcT(welch);
this.#calcLevene();
}
#calcT(welch) {
const { mean: m1, n: n1, varianceSample: v1 } = this.samples[0];
const { mean: m2, n: n2, varianceSample: v2 } = this.samples[1];
if (welch) {
const se2 = v1 / n1 + v2 / n2;
const den = ((v1 / n1) ** 2) / (n1 - 1) + ((v2 / n2) ** 2) / (n2 - 1); // Саттертуэйт
this.t = (m1 - m2) / Math.sqrt(se2);
this.df = (se2 * se2) / den;
} else {
this.df = n1 + n2 - 2;
const sp2 = ((n1 - 1) * v1 + (n2 - 1) * v2) / this.df; // pooled variance
this.t = (m1 - m2) / Math.sqrt(sp2 * (1 / n1 + 1 / n2));
}
}
#calcLevene() { /** Levene’s test (на основе средних абсолютных отклонений; как в отчёте SPSS) */
const k = this.k;
const N = this.samples.reduce((s, g) => s + g.n, 0);
// Для каждой группы считаем |x_ij - mean_i|
const zGroups = this.samples.map(({ values, mean }) =>
values.map(v => Math.abs(v - mean))
);
const n = zGroups.map(z => z.length);
const zbar_i = zGroups.map((z, i) => z.reduce((a, b) => a + b, 0) / n[i]);
const zbar = zbar_i.reduce((s, zi, i) => s + n[i] * zi, 0) / N;
// ANOVA для z: SSB и SSW
const ssb = zbar_i.reduce((s, zi, i) => s + n[i] * (zi - zbar) ** 2, 0);
const ssw = zGroups.reduce((s, z, i) =>
s + z.reduce((acc, zij) => acc + (zij - zbar_i[i]) ** 2, 0), 0
);
const df1 = k - 1;
const df2 = N - k;
const msb = ssb / df1;
const msw = ssw / df2;
const F = msb / msw;
this.leveneF = F;
this.leveneDf1 = df1;
this.leveneDf2 = df2;
this.leveneP = 1 - CDF.f(F, df1, df2); // правохвостовая p
}
get p() { return 2 * (1 - CDF.t(Math.abs(this.t), this.df)); } /** Двухсторонний p-value для t */
get F() { return this.t * this.t; } /** F для различий средних (ANOVA-эквивалент): F = t^2 */
}