refactor: switch eslint configuration to strict type checking
[e-mobility-charging-stations-simulator.git] / src / utils / StatisticUtils.ts
CommitLineData
aa63c9b7 1import { isEmptyArray } from './Utils.js'
4884b8d3 2
c7ba22b7
JB
3/**
4 * Computes the average of the given data set.
5 *
6 * @param dataSet - Data set.
7 * @returns The average of the given data set.
8 * @internal
9 */
10export const average = (dataSet: number[]): number => {
11 if (Array.isArray(dataSet) && dataSet.length === 0) {
66a7748d 12 return 0
c7ba22b7
JB
13 }
14 if (Array.isArray(dataSet) && dataSet.length === 1) {
66a7748d 15 return dataSet[0]
c7ba22b7 16 }
66a7748d
JB
17 return dataSet.reduce((accumulator, nb) => accumulator + nb, 0) / dataSet.length
18}
c7ba22b7
JB
19
20/**
21 * Computes the median of the given data set.
22 *
23 * @param dataSet - Data set.
24 * @returns The median of the given data set.
25 * @internal
26 */
4884b8d3 27export const median = (dataSet: number[]): number => {
9bf0ef23 28 if (isEmptyArray(dataSet)) {
66a7748d 29 return 0
4884b8d3 30 }
66a7748d
JB
31 if (Array.isArray(dataSet) && dataSet.length === 1) {
32 return dataSet[0]
4884b8d3 33 }
66a7748d 34 const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
4884b8d3
JB
35 return (
36 (sortedDataSet[(sortedDataSet.length - 1) >> 1] + sortedDataSet[sortedDataSet.length >> 1]) / 2
66a7748d
JB
37 )
38}
4884b8d3
JB
39
40// TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree
41export const nthPercentile = (dataSet: number[], percentile: number): number => {
42 if (percentile < 0 && percentile > 100) {
66a7748d 43 throw new RangeError('Percentile is not between 0 and 100')
4884b8d3 44 }
9bf0ef23 45 if (isEmptyArray(dataSet)) {
66a7748d 46 return 0
4884b8d3 47 }
66a7748d 48 const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
4884b8d3 49 if (percentile === 0 || sortedDataSet.length === 1) {
66a7748d 50 return sortedDataSet[0]
4884b8d3
JB
51 }
52 if (percentile === 100) {
66a7748d 53 return sortedDataSet[sortedDataSet.length - 1]
4884b8d3 54 }
66a7748d
JB
55 const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1)
56 const percentileIndexInteger = Math.floor(percentileIndexBase)
5199f9fd 57 // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
aa63c9b7 58 if (sortedDataSet[percentileIndexInteger + 1] != null) {
4884b8d3
JB
59 return (
60 sortedDataSet[percentileIndexInteger] +
61 (percentileIndexBase - percentileIndexInteger) *
62 (sortedDataSet[percentileIndexInteger + 1] - sortedDataSet[percentileIndexInteger])
66a7748d 63 )
4884b8d3 64 }
66a7748d
JB
65 return sortedDataSet[percentileIndexInteger]
66}
4884b8d3 67
66b40cf3
JB
68/**
69 * Computes the sample standard deviation of the given data set.
70 *
71 * @param dataSet - Data set.
72 * @param dataSetAverage - Average of the data set.
73 * @returns The sample standard deviation of the given data set.
74 * @see https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation
75 * @internal
76 */
975d0a94
JB
77export const stdDeviation = (
78 dataSet: number[],
66a7748d 79 dataSetAverage: number = average(dataSet)
975d0a94 80): number => {
66b40cf3 81 if (isEmptyArray(dataSet)) {
66a7748d 82 return 0
66b40cf3
JB
83 }
84 if (Array.isArray(dataSet) && dataSet.length === 1) {
66a7748d 85 return 0
66b40cf3 86 }
8f60746c 87 return Math.sqrt(
4e1c771b 88 dataSet.reduce((accumulator, num) => accumulator + Math.pow(num - dataSetAverage, 2), 0) /
66a7748d
JB
89 (dataSet.length - 1)
90 )
91}