import interquartileRange from "./interquartile_range.js";
import stddev from "./sample_standard_deviation.js";
const SQRT_2PI = Math.sqrt(2 * Math.PI);
/**
* [常用核函数](https://en.wikipedia.org/wiki/Kernel_(statistics)#Kernel_functions_in_common_use)
* @private
*/
const kernels = {
/**
* 高斯核函数
* @private
*/
gaussian: function (u) {
return Math.exp(-0.5 * u * u) / SQRT_2PI;
}
};
/**
* 常用带宽选择方法
* @private
*/
const bandwidthMethods = {
/**
* ["正态参考分布"经验法则](https://stat.ethz.ch/R-manual/R-devel/library/MASS/html/bandwidth.nrd.html),
* [Silverman经验法则](https://en.wikipedia.org/wiki/Kernel_density_estimation#A_rule-of-thumb_bandwidth_estimator)的常用实现
* @private
*/
nrd: function (x) {
let s = stddev(x);
const iqr = interquartileRange(x);
if (typeof iqr === "number") {
s = Math.min(s, iqr / 1.34);
}
return 1.06 * s * Math.pow(x.length, -0.2);
}
};
/**
* [核密度估计](https://en.wikipedia.org/wiki/Kernel_density_estimation)是
* 一种通过样本数据估计潜在概率分布形态的非参数方法
*
* @name kernelDensityEstimation
* @param X 样本值数组
* @param kernel 核函数。若提供函数,应返回非负值且积分为1。默认为高斯核
* @param bandwidthMethod 带宽选择方法或固定带宽值。默认为"nrd",即常用的["正态参考分布"经验法则](https://stat.ethz.ch/R-manual/R-devel/library/MASS/html/bandwidth.nrd.html)
* @returns {Function} 估计得到的[概率密度函数](https://en.wikipedia.org/wiki/Probability_density_function),时间复杂度为O(X.length)
*/
function kernelDensityEstimation(X, kernel, bandwidthMethod) {
let kernelFn;
if (kernel === undefined) {
kernelFn = kernels.gaussian;
} else if (typeof kernel === "string") {
if (!kernels[kernel]) {
throw new Error('未知的内核函数"' + kernel + '"');
}
kernelFn = kernels[kernel];
} else {
kernelFn = kernel;
}
let bandwidth;
if (typeof bandwidthMethod === "undefined") {
bandwidth = bandwidthMethods.nrd(X);
} else if (typeof bandwidthMethod === "string") {
if (!bandwidthMethods[bandwidthMethod]) {
throw new Error('未知的带宽选择方法"' + bandwidthMethod + '"');
}
bandwidth = bandwidthMethods[bandwidthMethod](X);
} else {
bandwidth = bandwidthMethod;
}
return function (x) {
let i = 0;
let sum = 0;
for (i = 0; i < X.length; i++) {
sum += kernelFn((x - X[i]) / bandwidth);
}
return sum / bandwidth / X.length;
};
}
export default kernelDensityEstimation;