perf: switch to SMA and SMM in performance statistics
[e-mobility-charging-stations-simulator.git] / src / utils / StatisticUtils.ts
1 import { isEmptyArray, isNullOrUndefined } from './Utils';
2
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 */
10 export const average = (dataSet: number[]): number => {
11 if (Array.isArray(dataSet) && dataSet.length === 0) {
12 return 0;
13 }
14 if (Array.isArray(dataSet) && dataSet.length === 1) {
15 return dataSet[0];
16 }
17 return dataSet.reduce((accumulator, nb) => accumulator + nb, 0) / dataSet.length;
18 };
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 */
27 export const median = (dataSet: number[]): number => {
28 if (isEmptyArray(dataSet)) {
29 return 0;
30 }
31 if (Array.isArray(dataSet) === true && dataSet.length === 1) {
32 return dataSet[0];
33 }
34 const sortedDataSet = dataSet.slice().sort((a, b) => a - b);
35 return (
36 (sortedDataSet[(sortedDataSet.length - 1) >> 1] + sortedDataSet[sortedDataSet.length >> 1]) / 2
37 );
38 };
39
40 // TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree
41 export const nthPercentile = (dataSet: number[], percentile: number): number => {
42 if (percentile < 0 && percentile > 100) {
43 throw new RangeError('Percentile is not between 0 and 100');
44 }
45 if (isEmptyArray(dataSet)) {
46 return 0;
47 }
48 const sortedDataSet = dataSet.slice().sort((a, b) => a - b);
49 if (percentile === 0 || sortedDataSet.length === 1) {
50 return sortedDataSet[0];
51 }
52 if (percentile === 100) {
53 return sortedDataSet[sortedDataSet.length - 1];
54 }
55 const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1);
56 const percentileIndexInteger = Math.floor(percentileIndexBase);
57 if (!isNullOrUndefined(sortedDataSet[percentileIndexInteger + 1])) {
58 return (
59 sortedDataSet[percentileIndexInteger] +
60 (percentileIndexBase - percentileIndexInteger) *
61 (sortedDataSet[percentileIndexInteger + 1] - sortedDataSet[percentileIndexInteger])
62 );
63 }
64 return sortedDataSet[percentileIndexInteger];
65 };
66
67 export const stdDeviation = (dataSet: number[], dataSetAverage?: number): number => {
68 dataSetAverage = dataSetAverage ?? average(dataSet);
69 const geometricDeviation = dataSet.reduce((accumulator, nb) => {
70 const deviation = nb - dataSetAverage!;
71 return accumulator + deviation * deviation;
72 }, 0);
73 return Math.sqrt(geometricDeviation / dataSet.length);
74 };