我从两个叠加的正态分布中创建了一些数据,然后应用 sklearn.neighbors.KernelDensity 和 scipy.stats.gaussian_kde 来估计密度函数。然而,使用相同的带宽(1.0)和相同的内核,两种方法产生了不同的结果。有人能解释一下这是什么原因吗?谢谢帮助。
您可以在下面找到重现此问题的代码:
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import gaussian_kde
import seaborn as sns
from sklearn.neighbors import KernelDensity
n = 10000
dist_frac = 0.1
x1 = np.random.normal(-5,2,int(n*dist_frac))
x2 = np.random.normal(5,3,int(n*(1-dist_frac)))
x = np.concatenate((x1,x2))
np.random.shuffle(x)
eval_points = np.linspace(np.min(x), np.max(x))
kde_sk = KernelDensity(bandwidth=1.0, kernel='gaussian')
kde_sk.fit(x.reshape([-1,1]))
y_sk = np.exp(kde_sk.score_samples(eval_points.reshape(-1,1)))
kde_sp = gaussian_kde(x, bw_method=1.0)
y_sp = kde_sp.pdf(eval_points)
sns.kdeplot(x)
plt.plot(eval_points, y_sk)
plt.plot(eval_points, y_sp)
plt.legend(['seaborn','scikit','scipy'])
如果我将Scipy带宽更改为0.25,则两种方法的结果看起来大致相同。
3条答案
按热度按时间sirbozc51#
在scipy.stats.gaussian_kde和sklearn.neighbors.KernelDensity中,带宽的含义是不同的。Scipy.stats.gaussian_kde使用带宽因子https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.gaussian_kde.html。对于一维内核密度估计,应用以下公式:
sklearn.neighbors.KernelDensity的带宽= scipy.stats.gaussian_kde的带宽因子 * 样本的标准偏差
对于您的估计,这可能意味着您的标准差等于4。
我想参考Getting bandwidth used by SciPy's gaussian_kde function以了解更多信息。
krugob8w2#
老实说,我不知道为什么,但使用scipy超参数**bw_method='scott'**使它的工作方式与seborn完全相同。
所以,这似乎都是关于超参数的。我们可以通过深入理解它们来找出原因,但同时只需使用“scott”或“silverman”,而不是使用随机标量。
mpbci0fu3#
请增加“随机正态”的大小。您的数据点太少。请尝试使用
n=500000
并检查结果。