一、关于核密度估计带宽的一些说明
进行核密度估计Kernel Density Estimation(KDE)时,带宽bw的选取尤为重要,不同的带宽会对结果造成很大的影响。利用seaborn库中kdeplot()绘制kde曲线时,可以选择scott和silverman两种自适应带宽方法。
二、例图
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
import statsmodels.nonparametric.api as smnp
def kde_test(data, kernel, bw, gridsize, cut):
"""
:param data:有限样本数据
:param kernel:核函数
:param bw:带宽
:param gridsize:绘制拟合曲线中的离散点数;可理解为精度,会改变kde曲线的圆滑程度
:param cut: 源代码说明——Draw the estimate to cut * bw from the extreme data points.
:return: kde估计曲线的x、y坐标
"""
fft = kernel == "gau"
kde = smnp.KDEUnivariate(data)
kde.fit(kernel, bw, fft, gridsize=gridsize, cut=cut)
return kde.support, kde.density
class Kdefitplot:
def __init__(self, data, kernel='gau', bw="scott", legends=None, labels=None, fsize=(10, 6.18), show_point=False, show_info=True):
"""
:param data:以列表格式存储的数据
:param kernel:密度估计选用的核函数,可选{'gau'|'cos'|'biw'|'epa'|'tri'|‘triw’},默认为'gau'
:param bw:密度估计选用的自适应带宽方法,可选{'scott'|'silverman'|scalar|pair of scalars},默认为"scott"
:param legends: 图例名,默认为 "概率密度", "kde曲线", "最大值点"
:param labels:坐标轴标题名,默认为 "数据x", "概率密度"
:param show_info:是否显示拟合结果信息,默认为True
:param show_point:在kde曲线上显示目标点(最大值点),默认为False
"""
self.data = data
self.kernel = kernel
self.bw = bw
if legends is None:
legends = ["概率密度", "kde曲线", "最大值点"]
if labels is None:
labels = ["样本数据", "概率密度"]
self.legends = legends
self.labels = labels
self.fsize = fsize
self.show_info = show_info
self.show_point = show_point
self.gridsize = 100
self.cut = 3
def change_legend(self, new_legends):
# 更改图例
self.legends = new_legends
def change_label(self, new_labels):
# 更改坐标轴标题
self.labels = new_labels
def draw_plot(self):
# 利用seaborn库对字体大小进行统一设置,为fgsize[1]的0.12倍,即画布纵向大小为1000时,font_scale=1.2
sns.set_style("darkgrid")
sns.set_context("talk", font_scale=self.fsize[1] * 0.15)
plt.rcParams['font.sans-serif'] = ['SimHei']
matplotlib.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=self.fsize)
# 绘制频率直方图
sns.distplot(self.data, label=self.legends[0], rug=True, kde=True, kde_kws={"color": "g", "lw": 0})
# 以gau为核函数,scott为带宽估计方法
sns.kdeplot(self.data,
kernel=self.kernel,
bw=self.bw,
label=self.legends[1],
color="r",
linewidth=self.fsize[0]*0.2
)
# 计算kde曲线的x、y值
kdefit_x, kdefit_y = kde_test(self.data,
self.kernel,
self.bw,
gridsize=self.gridsize,
cut=self.cut)
# point为kde曲线最大值点
point = np.where(kdefit_y == np.max(kdefit_y))
# 在kde曲线上显示目标点,格式为黑色实心圆
if self.show_point:
plt.plot(kdefit_x[point], kdefit_y[point], "o", color='k', linewidth=self.fsize[0]*0.4, label=self.legends[2])
# 打印统计信息
if self.show_info:
# 显示核密度估计信息:kernel为核函数、bw为自适应带宽方法、point为kde曲线最大值点
# 基本统计信息:Size为样本数据点个数、Average为平均值、Q25/Q50/Q75分别为25%/50%/75%分位数
q25, q50, q75 = [round(q, 4) for q in np.percentile(self.data, [25, 50, 75])]
base_info = f"Size:{len(self.data)}\nAver:{np.mean(self.data)}\nQ25:{q25}; Q50:{q50}; Q75:{q75}\n\n"
kde_info = f"kernel:{self.kernel}\nbw:{self.bw}\nMax point appear in {kdefit_x[point]}\n"
print(base_info + kde_info)
# 设置x、y坐标轴标题
plt.xlabel(self.labels[0])
plt.ylabel(self.labels[1])
plt.legend()
plt.tight_layout()
if __name__ == "__main__":
iqs_1 = 100 + 15 * np.random.randn(500) # 待绘制直方图的数据
iqs_2 = 200 + 75 * np.random.randn(500)
kdeplot1 = Kdefitplot(iqs_1 + iqs_2, show_point=True)
kdeplot1.draw_plot()
plt.show()