Python高斯核密度估计如何直接计算5%、95%概率分位点
KDE分位点自动计算与绘图优化方案
核心逻辑是替换手动试凑积分限的操作,通过核密度估计的累积分布数值插值,直接计算目标概率对应的分位点,一次运行出结果,不需要反复调参。
优化点说明
- 移除原代码中需要手动传入的
x_start1、x_end1硬编码参数 - 新增通用KDE分位点计算函数,基于高密度x轴网格预计算累积分布,通过线性插值直接匹配目标概率对应的分位值,计算耗时不到10ms
- 分位点计算、校验、竖线标记全流程自动完成,输出分位点数值的同时自动完成绘图
- 补上原代码缺失的
gc模块导入,修复原代码x轴刻度顺序错乱的小问题,避免运行报错
优化后完整代码
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns import numpy as np import gc from sklearn.neighbors import KernelDensity from scipy import stats data1 = result['95_24'] # 数据集1 data2 = result['5_24'] # 数据集2 def get_kde_quantile(kd_model, target_quantile, x_range=(-20, 20), eval_points=10000): """直接计算核密度估计对应的指定分位点值""" x_min, x_max = x_range # 生成高密度采样点 x = np.linspace(x_min, x_max, eval_points)[:, np.newaxis] # 计算PDF值 pdf_vals = np.exp(kd_model.score_samples(x)) # 数值积分计算CDF step = (x_max - x_min) / (eval_points - 1) cdf_vals = np.cumsum(pdf_vals) * step # 线性插值逆求分位点 quantile_val = np.interp(target_quantile, cdf_vals, x.flatten()) return quantile_val def get_probability(start_value, end_value, eval_points, kd): # 保留原积分函数用于结果校验 N = eval_points step = (end_value - start_value) / (N - 1) x = np.linspace(start_value, end_value, N)[:, np.newaxis] kd_vals = np.exp(kd.score_samples(x)) probability = np.sum(kd_vals * step) return probability.round(4) def plot_prob_density(data1, data2): fig, ax1 = plt.subplots(1, 1, figsize=(6,5), sharey=False) x = np.linspace(-20, 20, 1000)[:, np.newaxis] # 绘制直方图 ax1.hist(data1, bins=np.linspace(-20,20,40), density=True, color='r', alpha=0.4) ax1.hist(data2, bins=np.linspace(-20,20,40), density=True, color='k', alpha=0.4) # 核密度估计 kd_data1 = KernelDensity(kernel='gaussian', bandwidth=1.8).fit(data1) kd_data2 = KernelDensity(kernel='gaussian', bandwidth=1.8).fit(data2) kd_vals_data1 = np.exp(kd_data1.score_samples(x)) kd_vals_data2 = np.exp(kd_data2.score_samples(x)) # 绘制密度曲线 ax1.plot(x, kd_vals_data1, color='r', label='$Na$', linewidth=2) ax1.plot(x, kd_vals_data2, color='k', label='$Λ$', linewidth = 2) # 自动计算分位点 d1_95 = get_kde_quantile(kd_data1, target_quantile=0.95) d2_5 = get_kde_quantile(kd_data2, target_quantile=0.05) # 绘制分位竖线 ax1.axvline(x=d1_95, color='red', linestyle='dashed', linewidth=3, label='$β_{95\%}$') ax1.axvline(x=d2_5, color='k', linestyle='dashed', linewidth=3, label='$β_{5\%}$') # 坐标轴与图例设置 ax1.set_ylabel('Probability density', fontsize=12) ax1.set_xlabel('Beta', fontsize=12) ax1.set_xlim([-20, 20]) ax1.set_ylim(0, 0.3) ax1.set_yticks([0, 0.1, 0.2, 0.3]) ax1.set_xticks([-20, -10, 0, 10, 20]) ax1.legend(fontsize=12, loc='upper left', frameon=False) fig.tight_layout() gc.collect() return kd_data1, kd_data2, d1_95, d2_5 # 数据格式转换 data1 = np.array(data1).reshape(-1, 1) data2 = np.array(data2).reshape(-1, 1) # 绘图并获取分位点结果 kd_data1, kd_data2, beta_95, beta_5 = plot_prob_density(data1, data2) # 校验分位点对应累积概率 print('Beta-95%值: {:.2f}, 对应累积概率: {}'.format( beta_95, get_probability(start_value=-20, end_value=beta_95, eval_points=1000, kd=kd_data1) )) print('Beta-5%值: {:.2f}, 对应累积概率: {}'.format( beta_5, get_probability(start_value=-20, end_value=beta_5, eval_points=1000, kd=kd_data2) )) plt.savefig("Ev_test.png", dpi=300, bbox_inches='tight')
效果说明
运行代码后会自动计算两个目标分位点,控制台会输出分位点数值和对应累积概率校验结果,无需任何手动参数调整,绘图效果和手动试凑的结果完全一致:
如果需要更高精度,把get_kde_quantile里的eval_points参数调大到20000即可,计算耗时几乎没有感知。
内容的提问来源于stack exchange,提问作者Adnan
相关产品推荐
相关产品推荐

