/**
* A mixin object containing commonly used statistical methods.
*/
export const StatisticsMixin = {
methods: {
movingAverage(buffer, value, windowSize) {
buffer.push(value); // Add the new value to the buffer
if (buffer.length > windowSize) {
buffer.shift(); // Remove the oldest value if the buffer exceeds the window size
}
return buffer.reduce((sum, val) => sum + val, 0) / buffer.length; // Return the average
},
/**
* Calculates the trendline of an array of data points.
*
* @param {Array} arr - The array of data points.
* @param {string} timeField - The field representing the time in each data point.
* @param {string} valueField - The field representing the value in each data point.
* @returns {Object|null} - The trendline object containing the slope, intercept, and trendline points, or null if the array has less than 2 elements.
*/
calcTrendLine(arr, timeField, valueField, xScale) {
let n = arr.length;
if (arr.length < 2) return null;
let sumX = 0, sumY = 0, sumX2 = 0, sumXY = 0;
let time = xScale == 'time';
let offset = time ? this.parseTime(arr[0][timeField], true) : 0;
let numericTime, value;
for (let i = 0; i < n; i++) {
numericTime = time ? this.parseTime(arr[i][timeField], true) - offset : i;
value = arr[i][valueField];
sumX += numericTime;
sumY += value;
sumX2 += numericTime * numericTime;
sumXY += numericTime * value;
}
let slope = (n * sumXY - sumX * sumY) / (n * sumX2 - sumX * sumX);
let intercept = (sumY - slope * sumX) / n;
return {
slope: slope,
intercept: intercept,
trendline: [
{ x: time ? this.parseTime(arr[0][timeField], false) : arr[0][timeField], y: intercept },
{ x: time ? this.parseTime(arr[n-1][timeField], false) : arr[n-1][timeField], y: slope * numericTime + intercept }]
};
},
/**
* Calculates the standard deviation of an array of numbers.
*
* @param {number[]} arr - The array of numbers.
* @param {number} mean - The mean value of the array.
* @returns {number} The standard deviation.
*/
standardDeviation(arr, mean) {
let n = arr.length;
return Math.sqrt(arr.map(x => Math.pow(x - mean, 2)).reduce((a, b) => a + b) / n)
},
/**
* Calculates the median value of an array.
* @param {Array} arr - The input array.
* @returns {number} The median value.
*/
median(arr) {
const length = arr.length
const mid = Math.floor(length / 2);
if (length % 2 === 0) {
return (arr[mid - 1] + arr[mid]) / 2;
} else {
return arr[mid];
}
},
/**
* Calculates the q1 value of an array.
* @param {Array} arr - The input array.
* @returns {number} The q1 value.
*/
q1(arr) {
return this.median(arr.slice(0, Math.floor(arr.length / 2)));
},
/**
* Calculates the q3 value of an array.
* @param {Array} arr - The input array.
* @returns {number} The q3 value.
*/
q3(arr) {
return this.median(arr.slice(Math.ceil(arr.length / 2)));
},
/**
* Calculates the average value of an array.
* @param {number[]} arr - The array of numbers.
* @returns {number} The average value.
*/
average(arr) {
return arr.reduce((a, b) => a + b, 0) / arr.length;
},
/**
* Calculates various statistics for an array of numbers.
* @param {number[]} numbers - The array of numbers.
* @returns {Object} - An object containing the calculated statistics.
*/
statistics(numbers) {
const result = numbers.reduce((acc, current) => {
acc.min = Math.min(acc.min, current);
acc.max = Math.max(acc.max, current);
acc.sum += current;
return acc;
}, { min: Number.MAX_VALUE, max: Number.MIN_VALUE, sum: 0 });
result.mean = result.sum / numbers.length;
result.stdev = this.standardDeviation(numbers, result.mean);
result.median = this.median(numbers);
result.n = numbers.length;
result.q1 = this.q1(numbers);
result.q3 = this.q3(numbers);
return result;
},
/**
* Calculates the derivative of a series.
* @param {array} s - The series to calculate the derivative for. Contains objects with x and y properties.
*/
derivative(s) {
let d = [];
for (let i = 0; i < s.length; i++) {
if (i == 0 || i == s.length - 1) {
d.push({ x: s[i].x, y: 0 });
} else {
let dx = s[i + 1].x - s[i - 1].x;
let dy = s[i + 1].y - s[i - 1].y;
d.push({ x: s[i].x, y: dy / dx });
}
}
return d;
}
}
}