使用scipy高斯核密度估计计算CDF逆
Using scipy gaussian kernel density estimation to calculate CDF inverse
scipy.stats
中的gaussian_kde
函数有一个函数evaluate
可以returns一个输入点的PDF值。我正在尝试使用 gaussian_kde
来估计逆 CDF。动机是生成一些输入数据的 Monte Carlo 实现,这些数据的统计分布是使用 KDE 进行数值估计的。是否有绑定到 gaussian_kde
的方法可以达到此目的?
下面的示例显示了这对于高斯分布的情况应该如何工作。首先,我将展示如何进行 PDF 计算以设置我想要实现的特定 API:
import numpy as np
from scipy.stats import norm, gaussian_kde
npts_kde = int(5e3)
n = np.random.normal(loc=0, scale=1, size=npts_kde)
kde = gaussian_kde(n)
npts_sample = int(1e3)
x = np.linspace(-3, 3, npts_sample)
kde_pdf = kde.evaluate(x)
norm_pdf = norm.pdf(x)
是否有类似的简单方法来计算逆 CDF? norm
函数有一个非常方便的 isf
函数可以做到这一点:
cdf_value = np.sort(np.random.rand(npts_sample))
cdf_inv = norm.isf(1 - cdf_value)
kde_gaussian
有这样的功能吗?还是直接从已经实现的方法构造这样一个函数?
矢量形式的方法integrate_box_1d
can be used to compute the CDF, but it is not vectorized; you'll need to loop over points. If memory is not an issue, rewriting its source code (which is essentially just a call to special.ndtr
) 可能会加快处理速度。
from scipy.special import ndtr
stdev = np.sqrt(kde.covariance)[0, 0]
pde_cdf = ndtr(np.subtract.outer(x, n)).mean(axis=1)
plot(x, pde_cdf)
反函数的绘图将是 plot(pde_cdf, x)
。如果目标是计算特定点的反函数,请考虑使用 ,对 CDF 的计算值进行插值。
您可以使用一些 python 技巧来快速 memory-effective 估计 CDF(基于 this answer):
from scipy.special import ndtr
cdf = tuple(ndtr(np.ravel(item - kde.dataset) / kde.factor).mean()
for item in x)
它的工作速度与 一样快,但具有线性 (len(kde.dataset)
) space 复杂度而不是二次(实际上,len(kde.dataset) * len(x)
)复杂度。
接下来您要做的就是使用逆近似,例如,从statsmodels。
这个问题已经在其他答案中得到了回答,但我花了一段时间才想清楚所有的事情。这是最终解决方案的完整示例:
import numpy as np
from scipy import interpolate
from scipy.special import ndtr
import matplotlib.pyplot as plt
from scipy.stats import norm, gaussian_kde
# create kde
npts_kde = int(5e3)
n = np.random.normal(loc=0, scale=1, size=npts_kde)
kde = gaussian_kde(n)
# grid for plotting
npts_sample = int(1e3)
x = np.linspace(-3, 3, npts_sample)
# evaluate pdfs
kde_pdf = kde.evaluate(x)
norm_pdf = norm.pdf(x)
# cdf and inv cdf are available directly from scipy
norm_cdf = norm.cdf(x)
norm_inv = norm.ppf(x)
# estimate cdf
cdf = tuple(ndtr(np.ravel(item - kde.dataset) / kde.factor).mean()
for item in x)
# estimate inv cdf
inversefunction = interpolate.interp1d(cdf, x, kind='cubic', bounds_error=False)
fig, ax = plt.subplots(1, 3, figsize=(6, 3))
ax[0].plot(x, norm_pdf, c='k')
ax[0].plot(x, kde_pdf, c='r', ls='--')
ax[0].set_title('PDF')
ax[1].plot(x, norm_cdf, c='k')
ax[1].plot(x, cdf, c='r', ls='--')
ax[1].set_title('CDF')
ax[2].plot(x, norm_inv, c='k')
ax[2].plot(x, inversefunction(x), c='r', ls='--')
ax[2].set_title("Inverse CDF")
scipy.stats
中的gaussian_kde
函数有一个函数evaluate
可以returns一个输入点的PDF值。我正在尝试使用 gaussian_kde
来估计逆 CDF。动机是生成一些输入数据的 Monte Carlo 实现,这些数据的统计分布是使用 KDE 进行数值估计的。是否有绑定到 gaussian_kde
的方法可以达到此目的?
下面的示例显示了这对于高斯分布的情况应该如何工作。首先,我将展示如何进行 PDF 计算以设置我想要实现的特定 API:
import numpy as np
from scipy.stats import norm, gaussian_kde
npts_kde = int(5e3)
n = np.random.normal(loc=0, scale=1, size=npts_kde)
kde = gaussian_kde(n)
npts_sample = int(1e3)
x = np.linspace(-3, 3, npts_sample)
kde_pdf = kde.evaluate(x)
norm_pdf = norm.pdf(x)
是否有类似的简单方法来计算逆 CDF? norm
函数有一个非常方便的 isf
函数可以做到这一点:
cdf_value = np.sort(np.random.rand(npts_sample))
cdf_inv = norm.isf(1 - cdf_value)
kde_gaussian
有这样的功能吗?还是直接从已经实现的方法构造这样一个函数?
矢量形式的方法integrate_box_1d
can be used to compute the CDF, but it is not vectorized; you'll need to loop over points. If memory is not an issue, rewriting its source code (which is essentially just a call to special.ndtr
) 可能会加快处理速度。
from scipy.special import ndtr
stdev = np.sqrt(kde.covariance)[0, 0]
pde_cdf = ndtr(np.subtract.outer(x, n)).mean(axis=1)
plot(x, pde_cdf)
反函数的绘图将是 plot(pde_cdf, x)
。如果目标是计算特定点的反函数,请考虑使用
您可以使用一些 python 技巧来快速 memory-effective 估计 CDF(基于 this answer):
from scipy.special import ndtr
cdf = tuple(ndtr(np.ravel(item - kde.dataset) / kde.factor).mean()
for item in x)
它的工作速度与 len(kde.dataset)
) space 复杂度而不是二次(实际上,len(kde.dataset) * len(x)
)复杂度。
接下来您要做的就是使用逆近似,例如,从statsmodels。
这个问题已经在其他答案中得到了回答,但我花了一段时间才想清楚所有的事情。这是最终解决方案的完整示例:
import numpy as np
from scipy import interpolate
from scipy.special import ndtr
import matplotlib.pyplot as plt
from scipy.stats import norm, gaussian_kde
# create kde
npts_kde = int(5e3)
n = np.random.normal(loc=0, scale=1, size=npts_kde)
kde = gaussian_kde(n)
# grid for plotting
npts_sample = int(1e3)
x = np.linspace(-3, 3, npts_sample)
# evaluate pdfs
kde_pdf = kde.evaluate(x)
norm_pdf = norm.pdf(x)
# cdf and inv cdf are available directly from scipy
norm_cdf = norm.cdf(x)
norm_inv = norm.ppf(x)
# estimate cdf
cdf = tuple(ndtr(np.ravel(item - kde.dataset) / kde.factor).mean()
for item in x)
# estimate inv cdf
inversefunction = interpolate.interp1d(cdf, x, kind='cubic', bounds_error=False)
fig, ax = plt.subplots(1, 3, figsize=(6, 3))
ax[0].plot(x, norm_pdf, c='k')
ax[0].plot(x, kde_pdf, c='r', ls='--')
ax[0].set_title('PDF')
ax[1].plot(x, norm_cdf, c='k')
ax[1].plot(x, cdf, c='r', ls='--')
ax[1].set_title('CDF')
ax[2].plot(x, norm_inv, c='k')
ax[2].plot(x, inversefunction(x), c='r', ls='--')
ax[2].set_title("Inverse CDF")