我需要代码来执行 2D 内核密度估计 (KDE),而且我发现 SciPy 实现太慢了。所以,我写了一个基于 FFT 的实现,但有几件事让我感到困惑。(FFT 实现还强制执行周期性边界条件,这正是我想要的。)
该实现基于从样本创建一个简单的直方图,然后将其与高斯卷积。这是执行此操作并将其与 SciPy 结果进行比较的代码。
from numpy import *
from scipy.stats import *
from numpy.fft import *
from matplotlib.pyplot import *
from time import clock
ion()
#PARAMETERS
N = 512 #number of histogram bins; want 2^n for maximum FFT speed?
nSamp = 1000 #number of samples if using the ranom variable
h = 0.1 #width of gaussian
wh = 1.0 #width and height of square domain
#VARIABLES FROM PARAMETERS
rv = uniform(loc=-wh,scale=2*wh) #random variable that can generate samples
xyBnds = linspace(-1.0, 1.0, N+1) #boundaries of histogram bins
xy = (xyBnds[1:] + xyBnds[:-1])/2 #centers of histogram bins
xx, yy = meshgrid(xy,xy)
#DEFINE SAMPLES, TWO OPTIONS
#samples = rv.rvs(size=(nSamp,2))
samples = array([[0.5,0.5],[0.2,0.5],[0.2,0.2]])
#DEFINITIONS FOR FFT IMPLEMENTATION
ker = exp(-(xx**2 + yy**2)/2/h**2)/h/sqrt(2*pi) #Gaussian kernel
fKer = fft2(ker) #DFT of kernel
#FFT IMPLEMENTATION
stime = clock()
#generate normalized histogram. Note sure why .T is needed:
hst = histogram2d(samples[:,0], samples[:,1], bins=xyBnds)[0].T / (xy[-1] - xy[0])**2
#convolve histogram with kernel. Not sure why fftshift is neeed:
KDE1 = fftshift(ifft2(fft2(hst)*fKer))/N
etime = clock()
print "FFT method time:", etime - stime
#DEFINITIONS FOR NON-FFT IMPLEMTATION FROM SCIPY
#points to sample the KDE at, in a form gaussian_kde likes:
grid_coords = append(xx.reshape(-1,1),yy.reshape(-1,1),axis=1)
#NON-FFT IMPLEMTATION FROM SCIPY
stime = clock()
KDEfn = gaussian_kde(samples.T, bw_method=h)
KDE2 = KDEfn(grid_coords.T).reshape((N,N))
etime = clock()
print "SciPy time:", etime - stime
#PLOT FFT IMPLEMENTATION RESULTS
fig = figure()
ax = fig.add_subplot(111, aspect='equal')
c = contour(xy, xy, KDE1.real)
clabel(c)
title("FFT Implementation Results")
#PRINT SCIPY IMPLEMENTATION RESULTS
fig = figure()
ax = fig.add_subplot(111, aspect='equal')
c = contour(xy, xy, KDE2)
clabel(c)
title("SciPy Implementation Results")
上面有两组样本。1000个随机点用于基准测试并被注释掉;三点用于调试。
后一种情况的结果图在这篇文章的末尾。
以下是我的问题:
- 我可以避免直方图的 .T 和 KDE1 的 fftshift 吗?我不确定为什么需要它们,但是没有它们,高斯人会出现在错误的地方。
- 如何为 SciPy 定义标量带宽?高斯在两种实现中具有很大不同的宽度。
- 同样,即使我给 gaussian_kde 一个标量带宽,为什么 SciPy 实现中的高斯函数不是径向对称的?
- 我如何为 FFT 代码实现 SciPy 中可用的其他带宽方法?
(请注意,在 1000 个随机点的情况下,FFT 代码比 SciPy 代码快约 390 倍。)