如何标准化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
假设我有一个形状数组 (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