使用带有2维数据的scipy.stats.gaussian_kde

时间:2010-11-08 22:23:06

标签: scipy multidimensional-array kernel-density

我正在尝试使用the scipy.stats.gaussian_kde class来平滑使用纬度和经度信息收集的一些离散数据,因此它最终显示为与等高线图有点相似,其中高密度是峰值,低密度是山谷。

我很难将二维数据集放入gaussian_kde类。我已经玩弄了解它如何与1维数据一起工作,所以我认为2维将是这样的:

from scipy import stats
from numpy import array
data = array([[1.1, 1.1],
              [1.2, 1.2],
              [1.3, 1.3]])
kde = stats.gaussian_kde(data)
kde.evaluate([1,2,3],[1,2,3])

这就是说我[1.1, 1.1], [1.2, 1.2], [1.3, 1.3]有3分。我希望在x和y轴上使用宽度为1的1到3进行核密度估计。

创建gaussian_kde时,它一直给我这个错误:

raise LinAlgError("singular matrix")
numpy.linalg.linalg.LinAlgError: singular matrix

查看gaussian_kde的源代码,我意识到我正在考虑数据集的含义与维度的计算方式完全不同,但我找不到任何示例代码维度数据与模块一起使用。有人可以通过一些示例方法帮助我使用gaussian_kde多维数据吗?

4 个答案:

答案 0 :(得分:5)

This example似乎就是你要找的东西:

import numpy as np
import scipy.stats as stats
from matplotlib.pyplot import imshow

# Create some dummy data
rvs = np.append(stats.norm.rvs(loc=2,scale=1,size=(2000,1)),
                stats.norm.rvs(loc=0,scale=3,size=(2000,1)),
                axis=1)

kde = stats.kde.gaussian_kde(rvs.T)

# Regular grid to evaluate kde upon
x_flat = np.r_[rvs[:,0].min():rvs[:,0].max():128j]
y_flat = np.r_[rvs[:,1].min():rvs[:,1].max():128j]
x,y = np.meshgrid(x_flat,y_flat)
grid_coords = np.append(x.reshape(-1,1),y.reshape(-1,1),axis=1)

z = kde(grid_coords.T)
z = z.reshape(128,128)

imshow(z,aspect=x_flat.ptp()/y_flat.ptp())

enter image description here

显然,轴需要修理。

您还可以使用

执行数据的散点图
scatter(rvs[:,0],rvs[:,1])

enter image description here

答案 1 :(得分:4)

我认为你正在将核密度估计与插值或内核回归混合在一起。如果您有更多的积分样本,KDE会估算积分的分布。

我不确定你想要哪种插值,但是scipy.interpolate中的样条线或rbf会更合适。

如果你想要一维内核回归,那么你可以在scikits.statsmodels中找到一个有几个不同内核的版本。

更新:这是一个例子(如果这是你想要的)

>>> data = 2 + 2*np.random.randn(2, 100)
>>> kde = stats.gaussian_kde(data)
>>> kde.evaluate(np.array([[1,2,3],[1,2,3]]))
array([ 0.02573917,  0.02470436,  0.03084282])

gaussian_kde在行中具有变量,在列中具有观察值,因此与统计中的常规方向相反。在您的示例中,所有三个点都在一条线上,因此它具有完美的相关性。也就是说,我猜,奇异矩阵的原因。

调整阵列方向并添加一个小噪声,该示例有效,但看起来仍然非常集中,例如您附近没有任何采样点(3,3):

>>> data = np.array([[1.1, 1.1],
              [1.2, 1.2],
              [1.3, 1.3]]).T
>>> data = data + 0.01*np.random.randn(2,3)
>>> kde = stats.gaussian_kde(data)
>>> kde.evaluate(np.array([[1,2,3],[1,2,3]]))
array([  7.70204299e+000,   1.96813149e-044,   1.45796523e-251])

答案 2 :(得分:0)

顶部答案中发布的示例对我不起作用。我不得不稍微调整一下它现在有效:

import numpy as np
import scipy.stats as stats
from matplotlib import pyplot as plt

# Create some dummy data
rvs = np.append(stats.norm.rvs(loc=2,scale=1,size=(2000,1)),
                stats.norm.rvs(loc=0,scale=3,size=(2000,1)),
                axis=1)

kde = stats.kde.gaussian_kde(rvs.T)

# Regular grid to evaluate kde upon
x_flat = np.r_[rvs[:,0].min():rvs[:,0].max():128j]
y_flat = np.r_[rvs[:,1].min():rvs[:,1].max():128j]
x,y = np.meshgrid(x_flat,y_flat)
grid_coords = np.append(x.reshape(-1,1),y.reshape(-1,1),axis=1)

z = kde(grid_coords.T)
z = z.reshape(128,128)

plt.imshow(z,aspect=x_flat.ptp()/y_flat.ptp())
plt.show()

答案 3 :(得分:0)

我发现很难理解SciPy手册对gaussian_kde如何处理2D数据的描述。这是一个解释,旨在补充@endolith的示例。我将代码分为几个步骤并加上注释,以解释较不直观的部分。

首先,导入:

import numpy as np
import scipy.stats as st
from matplotlib.pyplot import imshow, show

创建一些虚拟数据:它们是“ X”和“ Y”点坐标的一维数组。

np.random.seed(142)  # for reproducibility
x = st.norm.rvs(loc=2, scale=1, size=2000)
y = st.norm.rvs(loc=0, scale=3, size=2000)

对于2D密度估计,gaussian_kde对象必须使用包含两行包含“ X”和“ Y”数据集的数组来初始化。用NumPy术语,我们将它们“垂直堆叠”:

xy = np.vstack((x, y))

因此,“ X”数据在第一行xy[0,:]中,而“ Y”数据在第二行xy[1,:]中,而xy.shape(2, 2000)。现在创建gaussian_kde对象:

dens = st.gaussian_kde(xy)

我们将在2D网格上评估估计的2D密度PDF。在NumPy中创建网格的方法不止一种。我在这里展示了一种与@endolith的方法不同(但在功能上等效)的方法:

gx, gy = np.mgrid[x.min():x.max():128j, y.min():y.max():128j]
gxy = np.dstack((gx, gy)) # shape is (128, 128, 2)

gxy是一个3-D数组,[i,j]的第gxy个元素包含对应的“ X”和“ Y”值的2元素列表:{{ 1}}的值为gxy[i, j]

我们必须在每个2D网格点上调用[ gx[i], gy[j] ](或dens()这是同一件事)。 NumPy为此具有非常优雅的功能:

dens.pdf()

换句话说,可调用的z = np.apply_along_axis(dens, 2, gxy) (也可能是dens)是在3D数组dens.pdf中沿axis=2(第三轴)调用的,值应以二维数组形式返回。唯一的问题是gxy的形状将是z,而不是我期望的(128,128,1)。请注意,documentation表示:

out的形状[返回值L.D.]与arr的形状相同,除了沿 轴尺寸。该轴已删除,并替换为新尺寸 等于func1d返回值的形状。所以如果func1d返回 标量输出将比arr少一维。

很可能(128,128)返回了一个1个元组,而不是我希望的标量。我没有进一步调查该问题,因为这很容易解决:

dens()

之后,我们可以生成图像:

z = z.reshape(128, 128)

这是图像。 (请注意,我也已经实现了@endolith的版本,并且得到了与该版本没有区别的图像。)

Output of the commands above