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