build(deps-dev): apply updates
[e-mobility-charging-stations-simulator.git] / src / utils / StatisticUtils.ts
CommitLineData
c17a8d29 1import { mean } from 'rambda'
c7ba22b7 2
c17a8d29
JB
3export const min = (...args: number[]): number =>
4 args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity)
5
6export const max = (...args: number[]): number =>
7 args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity)
4884b8d3
JB
8
9// TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree
10export const nthPercentile = (dataSet: number[], percentile: number): number => {
11 if (percentile < 0 && percentile > 100) {
66a7748d 12 throw new RangeError('Percentile is not between 0 and 100')
4884b8d3 13 }
b5977da8 14 if (Array.isArray(dataSet) && dataSet.length === 0) {
66a7748d 15 return 0
4884b8d3 16 }
66a7748d 17 const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
4884b8d3 18 if (percentile === 0 || sortedDataSet.length === 1) {
66a7748d 19 return sortedDataSet[0]
4884b8d3
JB
20 }
21 if (percentile === 100) {
66a7748d 22 return sortedDataSet[sortedDataSet.length - 1]
4884b8d3 23 }
452a4864
JB
24 const base = (percentile / 100) * (sortedDataSet.length - 1)
25 const baseIndex = Math.floor(base)
5199f9fd 26 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
452a4864 27 if (sortedDataSet[baseIndex + 1] != null) {
4884b8d3 28 return (
452a4864
JB
29 sortedDataSet[baseIndex] +
30 (base - baseIndex) * (sortedDataSet[baseIndex + 1] - sortedDataSet[baseIndex])
66a7748d 31 )
4884b8d3 32 }
452a4864 33 return sortedDataSet[baseIndex]
66a7748d 34}
4884b8d3 35
66b40cf3
JB
36/**
37 * Computes the sample standard deviation of the given data set.
38 *
39 * @param dataSet - Data set.
40 * @param dataSetAverage - Average of the data set.
41 * @returns The sample standard deviation of the given data set.
42 * @see https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation
43 * @internal
44 */
c17a8d29 45export const stdDeviation = (dataSet: number[], dataSetAverage: number = mean(dataSet)): number => {
736073f0 46 if (Array.isArray(dataSet) && (dataSet.length === 0 || dataSet.length === 1)) {
66a7748d 47 return 0
66b40cf3 48 }
8f60746c 49 return Math.sqrt(
4e1c771b 50 dataSet.reduce((accumulator, num) => accumulator + Math.pow(num - dataSetAverage, 2), 0) /
66a7748d
JB
51 (dataSet.length - 1)
52 )
53}