fix: only process worker message events when necessary
[e-mobility-charging-stations-simulator.git] / src / utils / StatisticUtils.ts
index bb9a847091decf2ca488f83ad8c3d9d579d6e7fd..4125ab06a9598dd7d6dd7812dbb0539ecc20ba69 100644 (file)
@@ -1,55 +1,54 @@
-import { Utils } from './Utils';
+import { mean } from 'rambda'
 
-export const median = (dataSet: number[]): number => {
-  if (Utils.isEmptyArray(dataSet)) {
-    return 0;
-  }
-  if (Array.isArray(dataSet) === true && dataSet.length === 1) {
-    return dataSet[0];
-  }
-  const sortedDataSet = dataSet.slice().sort((a, b) => a - b);
-  return (
-    (sortedDataSet[(sortedDataSet.length - 1) >> 1] + sortedDataSet[sortedDataSet.length >> 1]) / 2
-  );
-};
+export const min = (...args: number[]): number =>
+  args.reduce((minimum, num) => (minimum < num ? minimum : num), Infinity)
+
+export const max = (...args: number[]): number =>
+  args.reduce((maximum, num) => (maximum > num ? maximum : num), -Infinity)
 
 // TODO: use order statistics tree https://en.wikipedia.org/wiki/Order_statistic_tree
 export const nthPercentile = (dataSet: number[], percentile: number): number => {
   if (percentile < 0 && percentile > 100) {
-    throw new RangeError('Percentile is not between 0 and 100');
+    throw new RangeError('Percentile is not between 0 and 100')
   }
-  if (Utils.isEmptyArray(dataSet)) {
-    return 0;
+  if (Array.isArray(dataSet) && dataSet.length === 0) {
+    return 0
   }
-  const sortedDataSet = dataSet.slice().sort((a, b) => a - b);
+  const sortedDataSet = dataSet.slice().sort((a, b) => a - b)
   if (percentile === 0 || sortedDataSet.length === 1) {
-    return sortedDataSet[0];
+    return sortedDataSet[0]
   }
   if (percentile === 100) {
-    return sortedDataSet[sortedDataSet.length - 1];
+    return sortedDataSet[sortedDataSet.length - 1]
   }
-  const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1);
-  const percentileIndexInteger = Math.floor(percentileIndexBase);
-  if (!Utils.isNullOrUndefined(sortedDataSet[percentileIndexInteger + 1])) {
+  const percentileIndexBase = (percentile / 100) * (sortedDataSet.length - 1)
+  const percentileIndexInteger = Math.floor(percentileIndexBase)
+  // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
+  if (sortedDataSet[percentileIndexInteger + 1] != null) {
     return (
       sortedDataSet[percentileIndexInteger] +
       (percentileIndexBase - percentileIndexInteger) *
         (sortedDataSet[percentileIndexInteger + 1] - sortedDataSet[percentileIndexInteger])
-    );
+    )
   }
-  return sortedDataSet[percentileIndexInteger];
-};
+  return sortedDataSet[percentileIndexInteger]
+}
 
-export const stdDeviation = (dataSet: number[]): number => {
-  let totalDataSet = 0;
-  for (const data of dataSet) {
-    totalDataSet += data;
-  }
-  const dataSetMean = totalDataSet / dataSet.length;
-  let totalGeometricDeviation = 0;
-  for (const data of dataSet) {
-    const deviation = data - dataSetMean;
-    totalGeometricDeviation += deviation * deviation;
-  }
-  return Math.sqrt(totalGeometricDeviation / dataSet.length);
-};
+/**
+ * Computes the sample standard deviation of the given data set.
+ *
+ * @param dataSet - Data set.
+ * @param dataSetAverage - Average of the data set.
+ * @returns The sample standard deviation of the given data set.
+ * @see https://en.wikipedia.org/wiki/Unbiased_estimation_of_standard_deviation
+ * @internal
+ */
+export const stdDeviation = (dataSet: number[], dataSetAverage: number = mean(dataSet)): number => {
+  if (Array.isArray(dataSet) && (dataSet.length === 0 || dataSet.length === 1)) {
+    return 0
+  }
+  return Math.sqrt(
+    dataSet.reduce((accumulator, num) => accumulator + Math.pow(num - dataSetAverage, 2), 0) /
+      (dataSet.length - 1)
+  )
+}