如何在多变量/三维中实现核密度估计

时间:2015-06-07 18:00:35

标签: python numpy machine-learning scikit-learn kernel-density

我有类似下面的数据集,我试图找出具有最佳带宽的核密度估计。

data = np.array([[1, 4, 3], [2, .6, 1.2], [2, 1, 1.2],
         [2, 0.5, 1.4], [5, .5, 0], [0, 0, 0],
         [1, 4, 3], [5, .5, 0], [2, .5, 1.2]])

但我无法弄清楚如何接近它。还有如何找到Σ矩阵。

更新

我尝试使用scikit-learn工具包中的KDE函数找出单变量(1D)kde,

# kde function
def kde_sklearn(x, x_grid, bandwidth):
    kde = KernelDensity(kernel='gaussian', bandwidth=bandwidth).fit(x)
    log_pdf = kde.score_samples(x_grid[:, np.newaxis])
    return np.exp(log_pdf)

# optimal bandwidth selection
from sklearn.grid_search import GridSearchCV
grid = GridSearchCV(KernelDensity(), {'bandwidth': np.linspace(.1, 1.0, 30)}, cv=20)
grid.fit(x)
bw = grid.best_params_

# pdf using kde
pdf = kde_sklearn(x, x_grid, bw)
ax.plot(x_grid, pdf, label='bw={}'.format(bw))
ax.legend(loc='best')
plt.show()

任何人都可以帮助我将其扩展到多变量/在这种情况下是3D数据吗?

1 个答案:

答案 0 :(得分:2)

有趣的问题。您有几个选择:

  1. 继续使用scikit-learn
  2. 使用其他库。例如,如果您感兴趣的内核是高斯 - 那么您可以使用scipy.gaussian_kde,这可以说更容易理解/应用。这个技术有一个很好的例子in this question.
  3. 从第一原则开始。这非常困难,我不推荐它
  4. This blog post详细介绍了核密度估计(KDE)的各种库实现的相对优点。

    我将告诉你(在我看来 - 是的,这是基于意见的)是最简单的方法,我认为在你的情况下是选项2。

    注意 此方法使用链接文档中描述的经验法则来确定带宽。使用的确切规则是斯科特的规则。你提到的Σ矩阵让我觉得经验法则带宽选择对你来说没问题,但你也谈到了最佳带宽,你提出的例子使用交叉验证来确定最佳带宽。因此,如果此方法不适合您的目的 - 请在评论中告诉我。

    import numpy as np
    from scipy import stats
    data = np.array([[1, 4, 3], [2, .6, 1.2], [2, 1, 1.2],
             [2, 0.5, 1.4], [5, .5, 0], [0, 0, 0],
             [1, 4, 3], [5, .5, 0], [2, .5, 1.2]])
    
    data = data.T #The KDE takes N vectors of length K for K data points
                  #rather than K vectors of length N
    
    kde = stats.gaussian_kde(data)
    
    # You now have your kde!!  Interpreting it / visualising it can be difficult with 3D data
    # You might like to try 2D data first - then you can plot the resulting estimated pdf
    # as the height in the third dimension, making visualisation easier.
    
    # Here is the basic way to evaluate the estimated pdf on a regular n-dimensional mesh
    # Create a regular N-dimensional grid with (arbitrary) 20 points in each dimension
    minima = data.T.min(axis=0)
    maxima = data.T.max(axis=0)
    space = [np.linspace(mini,maxi,20) for mini, maxi in zip(minima,maxima)]
    grid = np.meshgrid(*space)
    
    #Turn the grid into N-dimensional coordinates for each point
    #Note - coords will get very large as N increases...
    coords = np.vstack(map(np.ravel, grid))
    
    #Evaluate the KD estimated pdf at each coordinate
    density = kde(coords)
    
    #Do what you like with the density values here..
    #plot them, output them, use them elsewhere...
    

    警告

    根据您的具体问题,这可能会产生可怕的结果。要记住的事情显然是:

    1. 随着您的维度数量的增加,您想要的观察数据点的数量必须呈指数增长 - 您在3个维度中的9个点的样本数据非常稀疏 - 尽管我假设这些点表示在事实上你还有很多。

    2. 正如主体所述 - 带宽是以特定方式选择的 - 这可能导致估计的pdf过度(或可以想象但不太可能) - 平滑。