我正在尝试比较二维数组的sklearn.neighbors.KernelDensity与scipy.stats.gaussian_kde的性能。
从这篇文章中,我看到带宽(bw)在每个函数中的处理方式不同。这篇文章提供了一个设置正确 bw in 的方法,scipy
因此它将等同于sklearn
. 基本上它将 bw 除以样本标准偏差。结果是这样的:
# For sklearn
bw = 0.15
# For scipy
bw = 0.15/x.std(ddof=1)
x
我用来获取 KDE 的示例数组在哪里。这在 1D 中工作得很好,但我不能让它在 2D 中工作。
这MWE
是我得到的:
import numpy as np
from scipy import stats
from sklearn.neighbors import KernelDensity
# Generate random data.
n = 1000
m1, m2 = np.random.normal(0.2, 0.2, size=n), np.random.normal(0.2, 0.2, size=n)
# Define limits.
xmin, xmax = min(m1), max(m1)
ymin, ymax = min(m2), max(m2)
# Format data.
x, y = np.mgrid[xmin:xmax:100j, ymin:ymax:100j]
positions = np.vstack([x.ravel(), y.ravel()])
values = np.vstack([m1, m2])
# Define some point to evaluate the KDEs.
x1, y1 = 0.5, 0.5
# -------------------------------------------------------
# Perform a kernel density estimate on the data using scipy.
kernel = stats.gaussian_kde(values, bw_method=0.15/np.asarray(values).std(ddof=1))
# Get KDE value for the point.
iso1 = kernel((x1,y1))
print 'iso1 = ', iso[0]
# -------------------------------------------------------
# Perform a kernel density estimate on the data using sklearn.
kernel_sk = KernelDensity(kernel='gaussian', bandwidth=0.15).fit(zip(*values))
# Get KDE value for the point.
iso2 = kernel_sk.score_samples([[x1, y1]])
print 'iso2 = ', np.exp(iso2[0])
(iso2
显示为指数,因为sklearn
返回对数值)
我得到的结果iso1
是iso2
不同的,我不知道应该如何影响带宽(在任一函数中)以使它们相等(因为它们应该)。
添加
sklearn
聊天室(由 ep)建议我应该在计算内核之前缩放值,以便(x,y)
获得scipy
与sklearn
.
所以这就是我所做的:
# Scale values.
x_val_sca = np.asarray(values[0])/np.asarray(values).std(axis=1)[0]
y_val_sca = np.asarray(values[1])/np.asarray(values).std(axis=1)[1]
values = [x_val_sca, y_val_sca]
kernel = stats.gaussian_kde(values, bw_method=bw_value)
即:我在获取内核之前缩放了两个维度,scipy
同时保持获取内核的行sklearn
保持不变。
这给出了更好的结果,但获得的内核仍然存在差异:
其中红点是(x1,y1)
代码中的点。因此可以看出,密度估计的形状仍然存在差异,尽管非常小。也许这是可以实现的最好的?