UNPKG

@naturalcycles/js-lib

Version:

Standard library for universal (browser + Node.js) javascript

94 lines (93 loc) 2.52 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports._average = _average; exports._averageOrNull = _averageOrNull; exports._averageWeighted = _averageWeighted; exports._percentile = _percentile; exports._percentiles = _percentiles; exports._median = _median; const assert_1 = require("../error/assert"); const number_util_1 = require("../number/number.util"); /** * @returns Average of the array of numbers * * @example * * _average([1, 2, 3, 4]) * // 2.5 */ function _average(values) { (0, assert_1._assert)(values.length, '_average is called on empty array'); let total = 0; for (const n of values) total += n; return total / values.length; } /** * Same as _average, but safely returns null if input array is empty or nullish. */ function _averageOrNull(values) { return values?.length ? _average(values) : null; } /** * valuesArray and weightsArray length is expected to be the same. */ function _averageWeighted(values, weights) { let numerator = 0; let denominator = 0; // eslint-disable-next-line unicorn/no-for-loop for (let i = 0; i < values.length; i++) { numerator += values[i] * weights[i]; denominator += weights[i]; } return numerator / denominator; } /** * @example * * _percentile([1, 2, 3, 4], 50) * // 2.5 * * _percentile([1, 2, 3], 50) * // 2 * * _percentile([1, 2, 3], 100) * // 3 */ function _percentile(values, pc) { const sorted = (0, number_util_1._sortNumbers)(values); // Floating pos in the range of [0; length - 1] const pos = ((values.length - 1) * pc) / 100; const dec = pos % 1; const floorPos = Math.floor(pos); const ceilPos = Math.ceil(pos); return _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]); } /** * A tiny bit more efficient function than calling _percentile individually. */ function _percentiles(values, pcs) { const r = {}; const sorted = (0, number_util_1._sortNumbers)(values); for (const pc of pcs) { // Floating pos in the range of [0; length - 1] const pos = ((values.length - 1) * pc) / 100; const dec = pos % 1; const floorPos = Math.floor(pos); const ceilPos = Math.ceil(pos); r[pc] = _averageWeighted([sorted[floorPos], sorted[ceilPos]], [1 - dec, dec]); } return r; } /** * @example * * _median([1, 2, 3]) * // 2 * * _median([1, 2, 3, 4]) * // 2.5 */ function _median(values) { return _percentile(values, 50); }