@naturalcycles/js-lib
Version:
Standard library for universal (browser + Node.js) javascript
94 lines (93 loc) • 2.52 kB
JavaScript
;
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);
}