stats.standard-deviation@2.0.0
impl/typescript.ts
1,673 bytes · the TypeScript implementation · view raw
Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.
import { roundFloat } from "./math_round_float.ts"; ← from math.round-float ^1.0.0 · built alongside by fune
import { type DeviationKind } from "./stats_standard_deviation_types.ts";
/**
* Population or sample standard deviation, two-pass.
*
* The one-pass "mean of squares minus square of mean" shortcut cancels
* catastrophically on large, close values; two passes do not.
*/
export function standardDeviation(values: readonly number[], kind: DeviationKind, decimals: number): number {
if (!Array.isArray(values)) throw new TypeError("values must be a list of numbers");
for (const v of values) {
if (typeof v !== "number" || !Number.isFinite(v)) {
throw new TypeError(`values must be finite numbers, received ${v}`);
}
}
if (kind !== "population" && kind !== "sample") {
throw new RangeError(`unknown standard deviation kind "${kind}"`);
}
// Checked up front as well as in roundFloat (same wording), so a bad
// decimals is reported before an empty list, as in 1.x.
if (!Number.isInteger(decimals) || decimals < 0 || decimals > 12) {
throw new RangeError(`decimals must be a whole number from 0 to 12, received ${decimals}`);
}
const n = values.length;
if (n === 0) throw new RangeError("values must not be empty");
if (kind === "sample" && n < 2) {
throw new RangeError("sample standard deviation needs at least 2 values");
}
let sum = 0;
for (const v of values) sum += v;
const mean = sum / n;
let squares = 0;
for (const v of values) {
const d = v - mean;
squares += d * d;
}
// math.round-float rounds on the exact value of the double: 2.675 gives 2.67.
return roundFloat(Math.sqrt(squares / (kind === "sample" ? n - 1 : n)), decimals);
}