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