对于相同的数据,为什么scipy.stats.gaussian_kde()比seaborn.kde_plot()慢?

Vic*_*tor 5 python distribution gaussian scipy seaborn

在python 3.7中,我有这个numpy数组,其shape =(2,34900)。该数组是一个坐标列表,其中索引0代表X轴,索引1代表y轴。

当我使用seaborn.kde_plot()可视化此数据的分布时,在第i5代第7代上运行时,大约5-15秒钟即可得到结果。

但是,当我尝试运行以下代码时:

#Find the kernel for 
k = scipy.stats.kde.gaussian_kde(data, bw_method=.3)
#Define the grid
xi, yi = np.mgrid[0:1:2000*1j, 0:1:2000*1j]
#apply the function
zi = k(np.vstack([xi.flatten(), yi.flatten()]))
Run Code Online (Sandbox Code Playgroud)

这会为该数据找到高斯核并将其应用于我定义的网格,这需要花费更多时间。我无法运行完整的阵列,但是在大小为140的切片上运行时,大约需要40秒才能完成。

140大小的切片确实产生了一个有趣的结果,我能够使用进行可视化plt.pcolormesh()

我的问题是我在这里想念的是什么。如果我了解正确的情况,则scipy.stats.kde.gaussian_kde()可以使用来创建由数据定义的函数的估计。然后,我将该函数应用于2D空间,并得到它的Z分量。然后我在绘制Z分量。但是,该过程又有什么不同呢? seaborn.kde_plot()这会使代码花费更长的时间。

Scipy的实现只需要这样做的每一点:

for i in range(self.n):
    diff = self.dataset[:, i, newaxis] - points
    tdiff = dot(self.inv_cov, diff)
    energy = sum(diff*tdiff,axis=0) / 2.0
    result = result + exp(-energy)
Run Code Online (Sandbox Code Playgroud)

Imp*_*est 7

Seaborn通常有两种方法来计算双变量kde。如果可用,则使用statsmodels,如果不可用,则使用scipy

密码与问题中显示的相似。它使用scipy.stats.gaussian_kde。statsmodels代码使用statsmodels.nonparametric.api.KDEMultivariate

但是,为了进行合理的比较,两种方法都需要采用相同的网格大小。seaborn的标准网格大小为100点。

import numpy as np; np.random.seed(42)
import seaborn.distributions as sd

N = 34900
x = np.random.randn(N)
y = np.random.randn(N)
bw="scott"
gridsize=100
cut=3
clip = [(-np.inf, np.inf), (-np.inf, np.inf)]

f = lambda x,y : sd._statsmodels_bivariate_kde(x, y, bw, gridsize, cut, clip)
g = lambda x,y : sd._scipy_bivariate_kde(x, y, bw, gridsize, cut, clip)
Run Code Online (Sandbox Code Playgroud)

如果我们计时这两个功能,

# statsmodels
%timeit f(x,y)  # 1 loop, best of 3: 16.4 s per loop
# scipy
%timeit g(x,y)  # 1 loop, best of 3: 8.67 s per loop
Run Code Online (Sandbox Code Playgroud)

因此,Scipy的速度是statsmodels的两倍(seaborn默认值)。问题中的代码之所以要花费这么长的时间,是因为使用了大小为2000的网格代替了大小为100的网格。

看到这些结果,人们实际上会倾向于使用scipy而不是statsmodels。不幸的是,它不允许选择使用哪个。因此,需要手动设置相应的标志。

import seaborn.distributions as sd
sd._has_statsmodels = False
# plot kdeplot with scipy.stats.kde.gaussian_kde
sns.kdeplot(x,y)
Run Code Online (Sandbox Code Playgroud)


Vic*_*tor -2

看来seaborn只是采集了我的数据样本。由于尺寸较小,因此可以少量完成。另一方面,SciPy 在其处理中使用每一个点。因此,随着我使用的数据集的大小,它需要更长的时间。