如何标准化scikit learn的kde?

How to normalize kde of scikit learn?

假设我有一个形状数组 (100000,1),表示变量 X 在 0 和 1 之间均匀分布的样本。 我想估计这个变量的概率密度,我使用 Scikit-Learn KernelDensity 来做到这一点。

问题是我只得到一个未标准化的结果。概率密度的积分总和不为1。我应该怎么做才能自动归一化?我做错了什么吗?

def kde_sklearn(data, grid, **kwargs):
    """
    Kernel Density Estimation with Scikit-learn

    Parameters
    ----------
    data : numpy.array
        Data points used to compute a density estimator. It
        has `n x p` dimensions, representing n points and p
        variables.
    grid : numpy.array
        Data points at which the desity will be estimated. It
        has `m x p` dimensions, representing m points and p
        variables.

    Returns
    -------
    out : numpy.array
        Density estimate. Has `m x 1` dimensions
    """
    kde_skl = KernelDensity(**kwargs)
    kde_skl.fit(data)
    # score_samples() returns the log-likelihood of the samples
    log_pdf = kde_skl.score_samples(grid)
    return np.exp(log_pdf) 

X = np.random.uniform(0,1,1000).reshape(-1,1)
X1 = np.linspace(0,1,100)[:,np.newaxis]

kde_sklearn(X,X1,kernel='tophat')

Out[43]: 
array([0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5,
       0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5])

我希望向量为 1,因为积分之和应为 1。

这些是每个点的概率 - 如果

会发生什么

X1 = np.linspace(0,1,10000000)[:,np.newaxis]

?

你得到的数组不是distribution/sample来自随机变量

问题不在于规范化,我可以从示例中看出这一点。假设我 运行 以下代码使 KDE 适合标准正态分布的样本:

import numpy as np
import sklearn.neighbors as sn

# Sample from a standard normal distribution
XX = np.random.randn(1000).reshape(-1, 1)

# Fit a KDE
kde_sklg = sn.KernelDensity()
kde_sklg.fit(XX)

# Get estimated densities
XX1 = np.linspace(-4.0, 4.0, 100)[:, np.newaxis]
gdens = np.exp(kde_sklg.score_samples(XX1))

然后我可以使用梯形法则估计 PDF 下的面积,如下所示:

my_area = 0.0
for i in range(1,gdens.shape[0]):
    my_area += 0.5*(gdens[i] + gdens[i-1])*(XX1[i,0] - XX1[i-1,0])

我得到的估计面积 (my_area) 约为 0.996,非常接近 1。

问题是您的 KDE 没有处理统一 PDF 中出现在 0 和 1 处的跳转,所以它把它们抹掉了太多。然后,KDE 对 PDF 的估计值下的大约一半区域最终位于这些模糊区域下方。如果您将 X1 的值替换为 X2 = np.linspace(-1,2,200)[:,np.newaxis],您可以看到 KDE 在区间 [-1,0] 和[1,2].

我认为发布的答案不清楚,因此我提供另一个答案。

简而言之,integral总和为1,而不是概率。下面我展示了2种方法来获得确实等于1的积分。

import numpy as np
from sklearn.neighbors import KernelDensity

np.random.seed(1)

# some uniform data
X = np.random.uniform(-5,5,100).reshape(-1,1)

# grid to be used later0
grid = np.linspace(-5,5,1000)[:,np.newaxis]

# fit using the data
kde = KernelDensity(kernel = 'tophat', bandwidth= 0.5).fit(X)

# get log probailities of the grid
log_dens = kde.score_samples(grid)

# transform log prob to prob
probs = np.exp(log_dens)

# Integrate
print(np.trapz(probs.ravel(), grid.ravel()))
0.9732232232232225

plt.hist(X, density=True, bins=30)
plt.plot(grid.ravel(),probs.ravel())
plt.show()

请注意,另一种获取积分的方法如下,因为我们在定义的网格中有相同的步骤:

np.sum(probs*np.diff(grid.ravel())[0])
0.9732232232232225