import chiSquaredDistributionTable from "./chi_squared_distribution_table.js";
import mean from "./mean.js";
/**
* [χ2 (卡方) 拟合优度检验](http://en.wikipedia.org/wiki/Goodness_of_fit#Pearson.27s_chi-squared_test)
* 使用一个拟合优度的度量,该度量是观测频率与期望频率之间差异的平方和,
* 并除以期望的观测数。由此产生的 χ2 统计量 `chiSquared` 可以与卡方分布进行比较,
* 以确定拟合优度。为了确定卡方分布的自由度,取观测频率的总数并减去估计参数的数量。
* 检验统计量近似服从自由度为 (k − c) 的卡方分布,其中 `k` 是非空单元格的数量,
* `c` 是分布中估计参数的数量。
*
* @param {Array<number>} data 数据样本
* @param {Function} distributionType 返回分布在某一点的函数:
* 例如二项分布、伯努利分布或泊松分布
* @param {number} significance 显著性水平
* @returns {number} 卡方拟合优度检验结果
* @example
* // 数据来自 William W. Hines 和 Douglas C. Montgomery 的《工程与管理科学中的概率与统计》中的泊松拟合优度示例 10-19
* var data1019 = [
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
* 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
* 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
* 2, 2, 2, 2, 2, 2, 2, 2, 2,
* 3, 3, 3, 3
* ];
* ss.chiSquaredGoodnessOfFit(data1019, ss.poissonDistribution, 0.05); //= false
*/
function chiSquaredGoodnessOfFit(data, distributionType, significance) {
// 根据样本数据估计加权平均值
const inputMean = mean(data);
// 计算 χ2 统计量的值
let chiSquared = 0;
// 假设分布中估计参数的数量,预计会在分布测试中提供。
// 由于从样本数据中估计 `lambda`,损失一个自由度。
const c = 1;
// 假设分布
// 生成假设分布
const hypothesizedDistribution = distributionType(inputMean);
const observedFrequencies = [];
const expectedFrequencies = [];
// 创建一个数组,保存样本数据的直方图,形式为 `{ 值: 出现次数 }`
for (let i = 0; i < data.length; i++) {
if (observedFrequencies[data[i]] === undefined) {
observedFrequencies[data[i]] = 0;
}
observedFrequencies[data[i]]++;
}
// 直方图可能是稀疏的——值之间可能存在间隙。因此我们遍历直方图,
// 确保间隙处用 0 填充,而不是未定义。
for (let i = 0; i < observedFrequencies.length; i++) {
if (observedFrequencies[i] === undefined) {
observedFrequencies[i] = 0;
}
}
// 创建一个数组,保存给定样本大小和假设分布的预期数据的直方图。
for (const k in hypothesizedDistribution) {
if (k in observedFrequencies) {
expectedFrequencies[+k] = hypothesizedDistribution[k] * data.length;
}
}
// 如果某个类别的期望频率小于 3,则合并类别。
// 此转换也应用于观测频率。
for (let k = expectedFrequencies.length - 1; k >= 0; k--) {
if (expectedFrequencies[k] < 3) {
expectedFrequencies[k - 1] += expectedFrequencies[k];
expectedFrequencies.pop();
observedFrequencies[k - 1] += observedFrequencies[k];
observedFrequencies.pop();
}
}
// 遍历观测频率与预期频率之间的平方差,累加 `chiSquared` 统计量。
for (let k = 0; k < observedFrequencies.length; k++) {
chiSquared +=
Math.pow(observedFrequencies[k] - expectedFrequencies[k], 2) /
expectedFrequencies[k];
}
// 计算自由度并查表以接受或拒绝假设分布的拟合优度。
// 自由度计算公式为 (类别区间数 - 估计参数数 - 1)
const degreesOfFreedom = observedFrequencies.length - c - 1;
return (
chiSquaredDistributionTable[degreesOfFreedom][significance] < chiSquared
);
}
export default chiSquaredGoodnessOfFit;