数据分布平滑化技术:核密度估计KDE解决直方图不连续问题

简介: 核密度估计(KDE)通过平滑处理解决直方图密度估计中的不连续问题,提供连续密度函数。其核心在于使用核函数对数据点进行加权,避免区间划分带来的信息丢失。带宽参数h影响估计效果,过小导致波动大,过大则过度平滑。常用核函数包括高斯核与Epanechnikov核,实际应用中可借助Statsmodels或Seaborn库快速实现。

直方图密度函数在密度函数估计中存在不连续性问题,即密度值在相邻区间边界处发生突变。为获得随机变量的连续密度函数估计,核密度估计(Kernel Density Estimation, KDE)提供了有效的解决方案。

核函数

核函数本质上是密度估计中用于平滑处理的概率密度函数,通常选择对称核函数。核函数必须满足以下基本性质:非负性、曲线下面积为1、以零为中心、具有非零方差。这些约束条件保证了核函数作为概率密度函数的有效性。

核函数的核心思想在于解决直方图估计的固有缺陷。在传统直方图中,诸如[1, 3)和[3, 5)的离散区间会导致2到3之间数据点信息的丢失,产生块状且不连续的估计结果。核密度估计通过在每个数据点上放置平滑曲线(如高斯核函数K(u))来替代固定区间,曲线峰值位于数据点位置,其扩散范围覆盖数据间隙。

点x处的密度估计需要计算x与各数据点的距离(以带宽h为度量单位),然后将这些标准化距离输入核函数。距离较近的点产生较高的核函数值,距离较远的点贡献相对较小。KDE的数学表达式为:

通过单个数据点示例来解析K(x — Xi / h)的工作机制。假设K(u)为高斯核函数:

针对直方图密度估计中使用的

waiting_times

数据,单个数据点的核函数表现如下:

 waiting_times = np.sort(waiting_times)  

K = lambda u: (2 * np.pi) ** (-0.5) * np.exp(-0.5 * u ** 2)  

plt.figure(figsize=(10, 6))  
plt.ylim(0, 0.001)  
plt.scatter(waiting_times, np.zeros_like(waiting_times), color='red', zorder=15)  

for X in np.random.choice(waiting_times, size=1, replace=False):  
    kernel = [(1/h) * K((x - X) / h) / len(waiting_times) for x in waiting_times]  
    # we will talk about h in a bit  
    plt.plot(waiting_times, kernel, color="#2C5B03")  
     plt.axvline(x=X, ymin=0, color='#D08770', linestyle='-', alpha=1, linewidth=1)


当X = 75时,计算远距离点x = 50的密度贡献值会显著降低。由于高斯分布的特性,该贡献值不为零但极小,可视为可忽略的贡献量。带宽参数h的大小直接影响估计质量:

h较小时,u值增大,将u输入核函数K(u)后,对于Xi = 75的核函数,x = 50位置的贡献变得极其微小。带宽选择是KDE成功应用的关键因素。过小的h值使每个核函数过于狭窄,导致密度估计出现过度波动和噪声,捕获随机扰动而非真实分布形状;过大的h值使核函数过于宽泛,造成过度平滑而丢失数据中的重要细节。合适的h值需要在噪声抑制和细节保留之间达到最优平衡。

Silverman经验法则

Silverman经验法则为带宽选择提供了实用的起始估计。该方法利用样本标准差σ和样本容量n计算初始带宽值。虽然并非对所有数据集都是最优选择,但在多数情况下能够有效平衡偏差和方差,因此常作为精细调优前的默认选择。

 waiting_times = np.sort(waiting_times)  

K = lambda u: (2 * np.pi) ** (-0.5) * np.exp(-0.5 * u ** 2)  

sigma = np.std(waiting_times)  
n = len(waiting_times)  
h = 1.06 * sigma * n**(-1/5)  

plt.figure(figsize=(10, 6))  
plt.ylim(0, 0.001)  
plt.scatter(waiting_times, np.zeros_like(waiting_times), color='red', zorder=15)  

for X in waiting_times:  
    kernel = [(1/h) * K((x - X) / h) / len(waiting_times) for x in waiting_times]  
    plt.plot(waiting_times, kernel, color="#2C5B03")  
    plt.axvline(x=X, ymin=0, color='#D08770', linestyle='-', alpha=1, linewidth=1)  

plt.savefig("kernel_densities.png", dpi=300)  
plt.tight_layout()  
 plt.show()

分析结果表明,样本数据均值x̄附近区域具有较高的密度值,这是因为该区域聚集了多个高核密度贡献;而远离数据点集中区域的位置(如数值10附近)则表现出较低的密度值,因为其仅接收到较低的核密度贡献。

KDE公式中距离尺度变换为h后,各核函数的面积从单位面积变为h。为恢复单位面积特性,需要将每个核函数除以h。随后对n个单位面积求和得到总面积n,通过除以n进行归一化处理,最终得到:

 waiting_times = np.sort(waiting_times)  

K = lambda u: (2 * np.pi) ** (-0.5) * np.exp(-0.5 * u ** 2)  

sigma = np.std(waiting_times)  
n = len(waiting_times)  
h = 1.06 * sigma * n**(-1/5)  

def f(x, h):  
    kernel_values = [(1/h) * K((x - X) / h) for X in waiting_times]  
    return np.sum(kernel_values) / len(waiting_times)  

pdf = [f(x, h) for x in waiting_times]  

plt.figure(figsize=(10, 6))  
plt.plot(waiting_times, pdf, color='#E15759', linewidth=2, label='Kernel Density Estimate')  
plt.xlabel("Waiting Time (minutes)", fontsize=14)  
plt.ylabel("Density", fontsize=14)  
plt.title("Kernel Density Estimation of Waiting Times", fontsize=16, pad=15)  
plt.legend()  
plt.tight_layout()  
plt.savefig("kde_plot.png", dpi=300)  
 plt.show()

高斯核函数虽为最常用选择,但并非唯一方案。Epanechnikov核函数对邻近x的数据点赋予更高权重,同时完全忽略距离过远的点,即在特定范围外其贡献降为零。这种特性使其在计算上更为高效,因为远距离数据点不会影响x处的密度计算。Epanechnikov核函数的表达式为:

观察可见,当距离Xi超过单位距离时,贡献值降为零。

均匀核函数

均匀核函数是另一种选择方案。在该核函数中,带宽窗口内的所有点均等贡献,窗口外的点不产生贡献。实际上,使用均匀核函数的核密度估计等价于直方图密度估计器(HDE)的平滑化版本。

实际应用中无需手动实现KDE算法,现有软件库已提供完整实现。Statsmodels库的实现与手工编码逻辑一致,但密度曲线更为平滑,因为其在精细网格点上评估函数而非仅在样本值处计算。Seaborn

kdeplot

函数默认使用高斯核函数并自动选择带宽参数,仅需一行代码即可生成平滑的密度曲线。

 from statsmodels.nonparametric.kde import KDEUnivariate  
import seaborn as sns  

kde = KDEUnivariate(waiting_times)  
kde.fit(kernel="gau") # gau = gaussian kernel, epa = epanechnikov kernel, uni = uniform kernel  

plt.figure(figsize=(10, 6))  
plt.plot(kde.support, kde.density, color='#E15759', label='Gaussian Kernel (Statsmodels)')  
sns.kdeplot(waiting_times, color='#4C78A8', label='Gaussian Kernel (Seaborn)')  
plt.xlabel("Waiting Time (minutes)", fontsize=14)  
plt.ylabel("Density", fontsize=14)  
plt.title("Different Kernels for KDE", fontsize=16, pad=15)  
plt.legend()  
plt.tight_layout()  
plt.savefig("kde.png", dpi=300)  
 plt.show()

总结

直方图提供了分布的粗略概念,但存在跳跃性和块状特征。核密度估计通过在数据点上放置平滑曲线并求和的方式解决了这一问题,提供了密度的连续视图。核函数形状在实践中的影响有限,但带宽参数h至关重要,它决定了曲线是否过于嘈杂或过于平滑。Statsmodels和Seaborn等库提供的一行代码实现使得手动编程变得不再必要,但理解其底层机制对于正确应用和参数调优仍然重要。

https://avoid.overfit.cn/post/deb03b1a58884b478d19c8f5b6507f93

作者:Mohith

目录
相关文章
freeswitch 默认拨号方案(下)
freeswitch默认拨号方案中(conf/dialplan/default.xml)设置了一些基本的测试功能和PBX电话系统功能 包含了分机互拨及简单IVR功能
|
4月前
|
定位技术 数据处理 API
手把手教你怎么做人口密度热力图
本文介绍了使用Python和ArcGIS绘制人口密度地图的方法。Python部分包括地图数据获取、格式转换、数据整合及可视化;ArcGIS部分涵盖地图投影、数据连接、人口密度计算与图例设置。同时提供了C++代码用于数据分割,并介绍了如何利用高德API获取地址经纬度,实现地图标注。
|
9月前
|
人工智能
「域名+AI」全新体验,等你来玩!
「域名+AI」全新体验,等你来玩!
290 3
「域名+AI」全新体验,等你来玩!
基于Dijkstra算法的最优行驶路线搜索matlab仿真,以实际城市复杂路线为例进行测试
使用MATLAB2022a实现的Dijkstra算法在城市地图上搜索最优行驶路线的仿真。用户通过鼠标点击设定起点和终点,算法规划路径并显示长度。测试显示,尽管在某些复杂情况下计算路径可能与实际有偏差,但多数场景下Dijkstra算法能找到接近最短路径。核心代码包括图的显示、用户交互及Dijkstra算法实现。算法基于图论,不断更新未访问节点的最短路径。测试结果证明其在简单路线及多数复杂城市路况下表现良好,但在交通拥堵等特殊情况下需结合其他数据提升准确性。
|
机器学习/深度学习 人工智能 算法
|
消息中间件 设计模式 监控
中间件事件总线(Event Bus)
【6月更文挑战第19天】
455 8
|
传感器 人工智能 算法
掌握C++中的状态-事件回调矩阵:打造强大的有限状态机
掌握C++中的状态-事件回调矩阵:打造强大的有限状态机
389 0
|
安全 API 数据安全/隐私保护
Django与第三方服务的集成:支付、邮件等
【4月更文挑战第15天】本文介绍了Django集成支付服务和邮件服务的方法。对于支付服务,包括选择支付网关(如支付宝、微信支付、Stripe)、安装Django库、配置参数、创建支付视图及处理支付结果。而对于邮件服务,涉及配置邮件服务器、使用`django.core.mail`发送邮件,以及集成SendGrid等第三方服务以增强邮件功能。集成这些服务能丰富Django应用功能,同时要注意安全性与可靠性。
|
算法 数据可视化 数据挖掘
密度聚类DBSCAN、主成分分析PCA算法讲解及实战(附源码)
密度聚类DBSCAN、主成分分析PCA算法讲解及实战(附源码)
416 0