/**
* The Statistics class provides simple statistical analysis for numeric data series.
* It filters out non-numeric (NaN), empty, and +/- Infinity values.
*
* @author rkoppe <roland.koppe@awi.de>
*/
export class Statistics {
min = null;
q25 = null;
mean = null;
median = null;
q75 = null;
max = null;
stddev = null;
var = null;
n = null;
sum = null;
/**
* Constructs a Statistics object and calculates statistical properties for the given data array.
*
* @param {number[]} data - The data array to calculate statistics for.
*/
constructor(data) {
if (!(data && data.length && (data.length > 0))) return this;
// copy array, don't sort in place, NaN to the end
data = data.slice().sort(function (a, b) {
if ((a == '') || isNaN(a) || !isFinite(a)) return 1;
if ((b == '') || isNaN(b) || !isFinite(b)) return -1;
return a - b;
});
let min = Number.MAX_VALUE;
let max = Number.MIN_VALUE;
let n = 0;
let sum = 0;
// simple statistics
for (let i = 0; i < data.length; i++) {
let value = data[i];
if ((value == '') || isNaN(value) || !isFinite(value)) break;
if (min > value) min = value;
if (max < value) max = value;
sum += value;
n++;
}
if (n == 0) return;
// slice data to not contain NaN and Infinity
data = data.slice(0, n);
this.min = min;
this.max = max;
this.n = n;
this.sum = sum;
this.mean = this.sum / this.n;
// variance and standard deviation
sum = 0;
for (let i = 0; i < data.length; i++) {
let value = data[i];
sum += Math.pow(value - this.mean, 2);
}
this.var = sum / (this.n - 1);
this.stddev = Math.sqrt(this.var);
// continous percentiles
this.q25 = this._percentile(0.25, data);
this.median = this._percentile(0.50, data);
this.q75 = this._percentile(0.75, data);
}
/**
* Calculates and returns the continuous percentile defined by
* given percent and sorted! data array.
*
* @param {*} percent
* @param {*} data
* @returns number
*/
_percentile(percent, data) {
if (data.length == 1) return data[0];
let pos = percent * (data.length - 1);
if (Number.isInteger(pos)) {
return data[pos];
} else {
pos = Math.floor(pos);
return (data[pos] + data[pos + 1]) / 2;
}
}
}