如何使用 Python 中的核密度估计生成 CDF?

1 python statistics kernel-density

我遇到过一些可以进行核密度估计的方法,这些方法将为数据样本提供 PDF:

  • KDEpy
  • sklearn.neighbors.KernelDensity
  • scipy.stats.gaussian_kde

使用上述任何一种方法我都可以生成 PDF,但是我想知道如何获取我正在生成的 PDF 的 CDF。在数学中,我知道您可以对 PDF 进行积分以获得 CDF,但问题是这些方法仅提供 x 和 y 点,而不是要积分的函数。

我想知道如何将给出的数据转换为 CDF 图,或者找到数据的 PDF 函数,然后积分以获得 CDF。或者使用另一种方法,其中输出是 CDF 而不是 PDF。

jla*_*rcy 5

MCVE

让我们创建一些虚拟数据来进行讨论:

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

np.random.seed(123)
data = stats.norm(loc=0, scale=1).rvs(10**4)
Run Code Online (Sandbox Code Playgroud)

这是该包的基本想法scipy.stats

高斯KDE

我们可以使用专用工具来估计 KDE,例如gaussian_kde

kde = stats.gaussian_kde(data)
Run Code Online (Sandbox Code Playgroud)

它公开了一个PDF要每次评估的函数x,但缺少CDF.

使用Kolmogorov-Smirnov检验检查样本,我们不能拒绝阈值为 10% 的原假设(两个分布相同):

stats.ks_2samp(data, kde.resample(100).squeeze())
# KstestResult(statistic=0.0969, pvalue=0.29163373800871994)
Run Code Online (Sandbox Code Playgroud)

连续变量

scipy.stats包还公开了一个可供rv_continous继承的通用类。如文档中所述:

rv_continuous 新的随机变量可以通过对类进行子类化并至少重新定义_pdf_cdf方法(标准化为位置 0 和尺度 1)来定义。

所以我们可以使用这个有目的的逻辑来填补空白。如果不考虑任何性能,它可以归结为:

class KDEDist(stats.rv_continuous):
    
    def __init__(self, kde, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._kde = kde
    
    def _pdf(self, x):
        return self._kde.pdf(x)
Run Code Online (Sandbox Code Playgroud)

然后我们使用实验性 KDE 创建底层对象。

X = KDEDist(kde)

stats.ks_2samp(data, X.rvs(size=100))  # This call is kind of intensive
# KstestResult(statistic=0.0625, pvalue=0.8113077271721811)
Run Code Online (Sandbox Code Playgroud)

现在我们可以自然地 - 至少在 API 调用方面 -也可以评估PDF和:CDF

fig, axe = plt.subplots()
axe.hist(data, density=1)
axe.plot(x, X.pdf(x))
axe.plot(x, X.cdf(x))
Run Code Online (Sandbox Code Playgroud)

它返回:

在此输入图像描述

性能考虑

请注意,这种方法可以回答您的问题,但并不具有性能。KDE 计算成本高昂,主要是因为内核跨越整个数据空间(高斯在无穷大达到零)。因此,没有截止的特征计算是基于每次评估时数据集的所有观察结果。

更改窗口函数可以极大地提高性能。例如:三角窗口将在整个数据集上具有固定的跨度,并减少数据集范围和大小的计算。

实施注意事项

阅读文档,似乎rv_continuous最初是为了实现具有分析定义的新连续变量而设计的。

无论如何,如果未实现(覆盖)底层方法,该类会为其他统计数据提供自动解析/集成。

选择此方法时,如果您希望使其性能更高、更稳健(数值稳定性),则需要实现缺失的逻辑。

直方图代替 KDE

如果您可以放宽KDE需求并满足直方图分布,那么您可以依赖它rv_histogram,它基本上基于分箱分布执行相同的操作:

hist = np.histogram(data, bins=100)
hist_dist = stats.rv_histogram(hist)

stats.ks_2samp(data, hist_dist.rvs(size=100))
# KstestResult(statistic=0.0577, pvalue=0.8778871545532821)
Run Code Online (Sandbox Code Playgroud)

返回:

在此输入图像描述

KDE直方图

如果理论上可以接受,我们可以通过从 KDE 创建预期直方图来混合这两种策略:

hist = np.histogram(data, bins=1000)
hist_kde = kde.pdf(hist[1][:-1] + np.diff(hist[1]))
hist_dist_kde = stats.rv_histogram([hist_kde, hist[1]])

stats.ks_2samp(data, hist_dist_kde.rvs(size=100))
# KstestResult(statistic=0.1067, pvalue=0.19541766226890545)
Run Code Online (Sandbox Code Playgroud)

然后,CDF 相对于 KDE 具有相对平滑度(它仍然是直方图),并且连续变量对象具有尽可能高的性能rv_histogram

在此输入图像描述