
在javascript中,当我们需要创建一个类来处理一组数量不定的输入数据时,关键在于如何将这些数据有效地传递给类的实例并存储起来。一个常见的误解是尝试在构造函数中使用解构赋值来直接分配多个独立的实例属性,例如 constructor(...inputs) { [this.input1, this.input2, ...rest] = inputs; }。然而,对于处理一个整体的数据集(如用于统计分析的数值数组),更推荐的做法是将整个数组作为一个单一的属性存储在实例上。
正确的方法是让构造函数接收一个数组作为参数,并将其赋值给一个实例属性。这样,类中的所有方法都可以通过 this.propertyName 访问到完整的数据集,而无需将数据作为参数重复传递。
class Statistics {
/**
* 构造函数接收一个数值数组作为输入。
* @param {number[]} inputs - 用于统计分析的数值数组。
*/
constructor(inputs) {
if (!Array.isArray(inputs) || inputs.some(isNaN)) {
throw new Error("Constructor expects an array of numbers.");
}
this.inputs = inputs;
}
// ... 后续的统计方法将操作 this.inputs
}通过这种方式,Statistics 类的任何实例都将拥有一个名为 inputs 的属性,其中包含所有待分析的原始数据。
一旦数据被存储在 this.inputs 中,我们就可以开始实现各种统计计算方法。这些方法将直接操作 this.inputs。
最基础的统计是获取数据的数量和总和。
立即学习“Java免费学习笔记(深入)”;
class Statistics {
// ... constructor ...
/**
* 获取数据集中元素的数量。
* @returns {number} 元素的数量。
*/
count() {
return this.inputs.length;
}
/**
* 计算数据集中所有元素的总和。
* @returns {number} 元素的总和。
*/
sum() {
return this.inputs.reduce((accumulator, currentValue) => accumulator + currentValue, 0);
}
/**
* 计算数据集中所有元素的平方和。
* 这是一个内部辅助方法,用于方差和标准差的计算。
* @returns {number} 元素的平方和。
* @private
*/
_sumSquares() {
return this.inputs.reduce((accumulator, currentValue) => accumulator + currentValue * currentValue, 0);
}
}最小值、最大值和极差(最大值与最小值之差)是衡量数据范围的重要指标。
class Statistics {
// ... count, sum, _sumSquares ...
/**
* 获取数据集中的最小值。
* @returns {number} 最小值。
*/
min() {
return Math.min(...this.inputs);
}
/**
* 获取数据集中的最大值。
* @returns {number} 最大值。
*/
max() {
return Math.max(...this.inputs);
}
/**
* 计算数据集的极差(最大值减最小值)。
* @returns {number} 极差。
*/
range() {
return this.max() - this.min();
}
}现在,我们来构建更复杂的统计量,如均值、中位数、众数、方差和标准差。
均值是所有数据点的总和除以数据点的数量。
class Statistics {
// ... min, max, range ...
/**
* 计算数据集的算术平均值。
* @returns {number} 均值。
*/
mean() {
const count = this.count();
if (count === 0) return 0; // 避免除以零
return this.sum() / count;
}
}中位数是排序后数据集中间位置的数值。如果数据点数量为偶数,则取中间两个数的平均值。
class Statistics {
// ... mean ...
/**
* 计算数据集的中位数。
* @returns {number} 中位数。
*/
median() {
const sorted = this.inputs.toSorted((a, b) => a - b); // 使用 toSorted() 避免修改原数组
const len = this.count();
if (len === 0) return 0;
// 位运算 >> 1 等同于 Math.floor(len / 2)
const mid = len >> 1;
if (len % 2 === 1) { // 奇数长度
return sorted[mid];
} else { // 偶数长度
return (sorted[mid - 1] + sorted[mid]) / 2;
}
}
}众数是数据集中出现频率最高的数值。可能存在多个众数。
class Statistics {
// ... median ...
/**
* 内部辅助方法:计算数据集中每个数值的频率。
* @returns {Array<{value: number, count: number}>} 包含数值及其频率的数组,按频率降序排列。
* @private
*/
_frequencies() {
const frequencyMap = new Map();
for (const value of this.inputs) {
frequencyMap.set(value, (frequencyMap.get(value) || 0) + 1);
}
// 将 Map 转换为数组,并按频率降序、数值降序排序
return [...frequencyMap.entries()]
.map(([value, count]) => ({ value, count }))
.toSorted((a, b) => b.count - a.count || b.value - a.value);
}
/**
* 获取数据集的众数及其出现次数。
* 注意:此方法只返回出现频率最高的一个众数。
* @returns {{mode: number, count: number} | null} 众数及其出现次数,如果数据集为空则返回 null。
*/
mode() {
const frequencies = this._frequencies();
if (frequencies.length === 0) {
return null;
}
// 返回频率最高的那个
return frequencies[0];
}
}方差衡量数据点与均值的离散程度,标准差是方差的平方根。
class Statistics {
// ... mode ...
/**
* 计算数据集的样本方差。
* 这里使用总体方差公式 (N),而不是样本方差 (N-1)。
* @returns {number} 方差。
*/
var() {
const len = this.count();
if (len === 0) return 0;
// 方差 = (平方和 / N) - 均值的平方
return this._sumSquares() / len - this.mean() ** 2;
}
/**
* 计算数据集的标准差。
* @returns {number} 标准差。
*/
std() {
return this.var() ** 0.5; // 方差的平方根
}
}频率分布显示每个数值出现的百分比。
class Statistics {
// ... std ...
/**
* 计算数据集中每个数值的百分比频率分布。
* @returns {Array<[number, number]>} 包含百分比频率和数值的数组,按频率降序排列。
*/
freqDist() {
const totalCount = this.count();
if (totalCount === 0) return [];
const frequencies = this._frequencies();
return frequencies.map(({ value, count }) =>
[parseFloat(((count / totalCount) * 100).toFixed(1)), value] // 保留一位小数
);
}
}为了方便查看所有统计结果,可以添加一个 describe 方法来打印所有计算出的统计量。
class Statistics {
// ... 所有上述方法 ...
/**
* 打印所有主要的统计量到控制台。
*/
describe() {
console.log('Count:', this.count());
console.log('Sum: ', this.sum());
console.log('Min: ', this.min());
console.log('Max: ', this.max());
console.log('Range: ', this.range());
console.log('Mean: ', this.mean());
console.log('Median: ', this.median());
const modeResult = this.mode();
console.log('Mode: ', modeResult ? `(${modeResult.value}, ${modeResult.count})` : 'N/A');
console.log('Variance: ', parseFloat(this.var().toFixed(2))); // 示例:保留两位小数
console.log('Standard Deviation: ', parseFloat(this.std().toFixed(2))); // 示例:保留两位小数
console.log('Frequency Distribution: ', this.freqDist());
}
}以下是如何使用 Statistics 类的完整示例:
// 完整的 Statistics 类定义
class Statistics {
constructor(inputs) {
if (!Array.isArray(inputs) || inputs.some(isNaN)) {
throw new Error("Constructor expects an array of numbers.");
}
this.inputs = inputs;
}
count() {
return this.inputs.length;
}
sum() {
return this.inputs.reduce((a, b) => a + b, 0);
}
_sumSquares() {
return this.inputs.reduce((a, b) => a + b * b, 0);
}
mean() {
const count = this.count();
if (count === 0) return 0;
return this.sum() / count;
}
min() {
return Math.min(...this.inputs);
}
max() {
return Math.max(...this.inputs);
}
range() {
return this.max() - this.min();
}
median() {
const sorted = this.inputs.toSorted((a, b) => a - b);
const len = this.count();
if (len === 0) return 0;
const mid = len >> 1; // 等同于 Math.floor(len / 2)
if (len % 2 === 1) { // 奇数长度
return sorted[mid];
} else { // 偶数长度
return (sorted[mid - 1] + sorted[mid]) / 2;
}
}
_frequencies() {
const frequencyMap = new Map();
for (const value of this.inputs) {
frequencyMap.set(value, (frequencyMap.get(value) || 0) + 1);
}
return [...frequencyMap.entries()]
.map(([value, count]) => ({ value, count }))
.toSorted((a, b) => b.count - a.count || b.value - a.value);
}
mode() {
const frequencies = this._frequencies();
if (frequencies.length === 0) {
return null;
}
return frequencies[0];
}
var() {
const len = this.count();
if (len === 0) return 0;
return this._sumSquares() / len - this.mean() ** 2;
}
std() {
return this.var() ** 0.5;
}
freqDist() {
const totalCount = this.count();
if (totalCount === 0) return [];
const frequencies = this._frequencies();
return frequencies.map(({ value, count }) =>
[parseFloat(((count / totalCount) * 100).toFixed(1)), value]
);
}
describe() {
console.log('Count:', this.count());
console.log('Sum: ', this.sum());
console.log('Min: ', this.min());
console.log('Max: ', this.max());
console.log('Range: ', this.range());
console.log('Mean: ', this.mean());
console.log('Median: ', this.median());
const modeResult = this.mode();
console.log('Mode: ', modeResult ? `(${modeResult.value}, ${modeResult.count})` : 'N/A');
console.log('Variance: ', parseFloat(this.var().toFixed(2)));
console.log('Standard Deviation: ', parseFloat(this.std().toFixed(2)));
console.log('Frequency Distribution: ', this.freqDist());
}
}
// 示例数据
const ages = [31, 26, 34, 37, 27, 26, 32, 32, 26, 27, 27, 24, 32, 33, 27, 25, 26, 38, 37, 31, 34, 24, 33, 29, 26];
// 创建 Statistics 实例
const statistics = new Statistics(ages);
// 调用 describe 方法输出所有统计结果
statistics.describe();通过上述设计,我们成功构建了一个功能完善且易于使用的 JavaScript 统计分析类。这种模块化的方法不仅使得代码结构清晰、易于维护,而且通过将数据作为实例属性存储,避免了在每个方法中重复传递数据,大大提高了代码的效率和可读性。这个类可以作为任何需要进行数值数据分析的JavaScript应用程序的基础组件。
以上就是JavaScript中构建统计分析类:处理可变参数数组与实现常用统计方法的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号