核密度估计(KDE对于这个问题)是一个更有效的工具。这个gaussian_kde估计方法可以被用来估计
单元或多元数据的PDF。它在数据呈单峰的时候工作的最好,但也可以在多峰情况下工作。
我们以一个最小数据集来观察gaussian_kde是如何工作的,以及带宽(bandwidth)的不同选择方式。
PDF对应的数据被以蓝线的形式显示在图像的底端(被称为毯图(rug plot))
我们看到在Scott规则以及Silverman规则下的结果几乎没有差异。以及带宽的选择相比较于
数据的稀少显得太宽。我们可以定义我们的带宽函数以获得一个更少平滑的结果。
... """We use Scott's Rule, multiplied by a constant factor."""
... return np.power(obj.n, -1./(obj.d+4)) * fac
>>> fig = plt.figure()
>>> ax = fig.add_subplot(111)
>>> ax.plot(x1, np.zeros(x1.shape), 'b+', ms=20) # rug plot
>>> kde3 = stats.gaussian_kde(x1, bw_method=my_kde_bandwidth)
>>> ax.plot(x_eval, kde3(x_eval), 'g-', label="With smaller BW")
>>> plt.show()
我们看到如果我们设置带宽为非常狭窄,则获得PDF的估计退化为围绕在数据点的简单的高斯和。
我们现在使用更真实的例子,并且看看在两种带宽选择规则中的差异。这些规则被认为在
正态分布上很好用,但即使是偏离正态的单峰分布上它也工作的很好。作为一个非正态分布,
我们采用5自由度的t分布。
下面我们看到这个一个宽一个窄的双峰分布。可以想到结果将难达到以十分近似,
因为每个峰需要不同的带宽去拟合。
>>> from functools import partial
>>> loc2, scale2, size2 = (2, 0.2, 50)
... np.random.normal(loc=loc2, scale=scale2, size=size2)])
>>> x_eval = np.linspace(x2.min() - 1, x2.max() + 1, 500)
>>> kde = stats.gaussian_kde(x2)
>>> kde2 = stats.gaussian_kde(x2, bw_method='silverman')
>>> kde3 = stats.gaussian_kde(x2, bw_method=partial(my_kde_bandwidth, fac=0.2))
>>> kde4 = stats.gaussian_kde(x2, bw_method=partial(my_kde_bandwidth, fac=0.5))
>>> pdf = stats.norm.pdf
>>> bimodal_pdf = pdf(x_eval, loc=loc1, scale=scale1) * float(size1) / x2.size + \
... pdf(x_eval, loc=loc2, scale=scale2) * float(size2) / x2.size
>>> fig = plt.figure(figsize=(8, 6))
>>> ax.plot(x2, np.zeros(x2.shape), 'b+', ms=12)
>>> ax.plot(x_eval, kde(x_eval), 'k-', label="Scott's Rule")
>>> ax.plot(x_eval, kde2(x_eval), 'b-', label="Silverman's Rule")
>>> ax.plot(x_eval, kde3(x_eval), 'g-', label="Scott * 0.2")
>>> ax.plot(x_eval, kde4(x_eval), 'c-', label="Scott * 0.5")
>>> ax.plot(x_eval, bimodal_pdf, 'r--', label="Actual PDF")
>>> ax.set_xlim([x_eval.min(), x_eval.max()])
>>> ax.legend(loc=2)
>>> ax.set_xlabel('x')
>>> ax.set_ylabel('Density')
>>> plt.show()
正如预想的,KDE并没有很好的趋近正确的PDF,因为双峰分布的形状不同。通过使用默认带宽
(Scott*0.5)我们可以做得更好,再使用更小的带宽将使平滑性受到影响。这里我们真正需要
的是非均匀(自适应)带宽。
多元估计
通过gaussian_kde我们可以像单元估计那样进行多元估计。我们现在来解决二元情况,
首先我们产生一些随机的二元数据。
然后我们对这些数据使用KDE:
>>> X, Y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
>>> positions = np.vstack([X.ravel(), Y.ravel()])
>>> values = np.vstack([m1, m2])
>>> Z = np.reshape(kernel.evaluate(positions).T, X.shape)
最后我们把估计的双峰分布以colormap形式显示出来,并且在上面画出每个数据点。