scipy.stats.sampling.

NumericalInversePolynomial#

class scipy.stats.sampling.NumericalInversePolynomial(dist, *, mode=None, center=None, domain=None, order=5, u_resolution=1e-10, random_state=None)#

基于多项式插值的 CDF 逆变换 (PINV)。

PINV 是数值反演的一种变体,其中使用牛顿插值公式逼近逆 CDF。区间 [0,1] 被分成几个子区间。在每个子区间中,逆 CDF 在节点 (CDF(x),x) 上构造,其中 x 是此子区间中的某些点。如果给定 PDF,则使用 5 个点的自适应高斯-洛巴托积分从给定 PDF 数值计算 CDF。子区间会分割,直到达到要求的精度目标。

该方法并不精确,因为它仅产生近似分布的随机变量。尽管如此,最大允许的近似误差可以设置为分辨率(当然,受机器精度的限制)。我们使用 u 误差 |U - CDF(X)| 来测量误差,其中 X 是对应于分位数 U 的近似百分位数,即 X = approx_ppf(U)。我们将最大容忍的 u 误差称为算法的 u 分辨率。

可以选择插值多项式的阶数和 u 分辨率。请注意,u 分辨率的值非常小是可能的,但这会增加设置步骤的成本。

必须在设置步骤中计算插值多项式。但是,它仅适用于有界域的分布;对于无界域的分布,尾部会被截断,使得尾部区域的概率与给定的 u 分辨率相比很小。

仅当 PDF 是单峰的或当 PDF 在两个峰值之间不消失时,插值多项式的构造才有效。

给定分布有一些限制

  • 分布的支持(即,PDF 严格为正的区域)必须是连通的。实际上,这意味着 PDF “不太小”的区域必须是连通的。单峰密度满足此条件。如果违反此条件,则可能会截断分布的域。

  • 当 PDF 以数值方式积分时,给定的 PDF 必须是连续的并且应该是平滑的。

  • PDF 必须是有界的。

  • 当分布具有重尾(此时逆 CDF 在 0 或 1 处变得非常陡峭)并且请求的 u 分辨率非常小时,该算法会出现问题。例如,当请求的 u 分辨率小于 1.e-12 时,柯西分布很可能会出现此问题。

参数:
dist对象

具有 pdflogpdf 方法的类实例,可以选择具有 cdf 方法。

  • pdf:分布的 PDF。PDF 的签名应为:def pdf(self, x: float) -> float,即,PDF 应接受 Python 浮点数并返回 Python 浮点数。它不需要积分到 1,即,PDF 不需要标准化。此方法是可选的,但是需要指定 pdflogpdf 中的一个。如果两者都给出,则使用 logpdf

  • logpdf:分布的 PDF 的对数。签名与 pdf 相同。类似地,可以忽略 PDF 的归一化常数的对数。此方法是可选的,但是需要指定 pdflogpdf 中的一个。如果两者都给出,则使用 logpdf

  • cdf:分布的 CDF。此方法是可选的。如果提供,它可以计算“u 误差”。请参阅 u_error。必须具有与 PDF 相同的签名。

mode浮点数,可选

分布的(精确)模式。默认为 None

center浮点数,可选

分布的模式或均值的近似位置。此位置提供有关 PDF 主要部分的一些信息,并用于避免数值问题。默认为 None

domain长度为 2 的列表或元组,可选

分布的支持。默认为 None。当 None

  • 如果分布对象 dist 提供了一个 support 方法,则它用于设置分布的域。

  • 否则,支持假定为 \((-\infty, \infty)\)

order整数,可选

插值多项式的阶数。有效阶数介于 3 和 17 之间。阶数越高,近似值的区间越少。默认为 5。

u_resolution浮点数,可选

设置最大容忍的 u 误差。u 分辨率的值必须至少为 1.e-15,最多为 1.e-5。请注意,大多数均匀随机数源的分辨率为 2-32= 2.3e-10。因此,1.e-10 的值会导致一种可以称为精确的反演算法。对于大多数模拟,最大误差的稍大的值也足够了。默认为 1e-10。

random_state{None, int, numpy.random.Generator,

一个 NumPy 随机数生成器,或用于生成均匀随机数流的底层 NumPy 随机数生成器的种子。如果 random_state 为 None(或 np.random),则使用 numpy.random.RandomState 单例。如果 random_state 是一个整数,则使用一个新的 RandomState 实例,并使用 random_state 作为种子。如果 random_state 已经是 GeneratorRandomState 实例,则使用该实例。

参考文献

[1]

Derflinger, Gerhard, Wolfgang Hörmann 和 Josef Leydold。“仅当密度已知时通过数值反演生成随机变量。” ACM Transactions on Modeling and Computer Simulation (TOMACS) 20.4 (2010): 1-25.

[2]

UNU.RAN 参考手册,第 5.3.12 节,“PINV - 基于多项式插值的 CDF 反演”,https://statmath.wu.ac.at/software/unuran/doc/unuran.html#PINV

示例

>>> from scipy.stats.sampling import NumericalInversePolynomial
>>> from scipy.stats import norm
>>> import numpy as np

要创建一个从标准正态分布中采样的生成器,请执行以下操作

>>> class StandardNormal:
...    def pdf(self, x):
...        return np.exp(-0.5 * x*x)
...
>>> dist = StandardNormal()
>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, random_state=urng)

创建生成器后,可以通过调用 rvs 方法从分布中抽取样本

>>> rng.rvs()
-1.5244996276336318

为了检查随机变量是否紧密跟随给定分布,我们可以查看其直方图

>>> import matplotlib.pyplot as plt
>>> rvs = rng.rvs(10000)
>>> x = np.linspace(rvs.min()-0.1, rvs.max()+0.1, 1000)
>>> fx = norm.pdf(x)
>>> plt.plot(x, fx, 'r-', lw=2, label='true distribution')
>>> plt.hist(rvs, bins=20, density=True, alpha=0.8, label='random variates')
>>> plt.xlabel('x')
>>> plt.ylabel('PDF(x)')
>>> plt.title('Numerical Inverse Polynomial Samples')
>>> plt.legend()
>>> plt.show()
../../_images/scipy-stats-sampling-NumericalInversePolynomial-1_00_00.png

如果设置期间可以使用精确的 CDF,则可以估计近似 PPF 的 u 误差。为此,在初始化期间传递具有分布的精确 CDF 的 dist 对象

>>> from scipy.special import ndtr
>>> class StandardNormal:
...    def pdf(self, x):
...        return np.exp(-0.5 * x*x)
...    def cdf(self, x):
...        return ndtr(x)
...
>>> dist = StandardNormal()
>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, random_state=urng)

现在,可以通过调用 u_error 方法来估计 u 误差。它运行蒙特卡洛模拟来估计 u 误差。默认情况下,使用 100000 个样本。要更改此设置,可以将样本数作为参数传递

>>> rng.u_error(sample_size=1000000)  # uses one million samples
UError(max_error=8.785994154436594e-11, mean_absolute_error=2.930890027826552e-11)

这将返回一个命名元组,其中包含最大 u 误差和平均绝对 u 误差。

可以通过降低 u 分辨率(允许的最大 u 误差)来减少 u 误差。

>>> urng = np.random.default_rng()
>>> rng = NumericalInversePolynomial(dist, u_resolution=1.e-12, random_state=urng)
>>> rng.u_error(sample_size=1000000)
UError(max_error=9.07496300328603e-13, mean_absolute_error=3.5255644517257716e-13)

请注意,这样做会增加设置时间。

可以通过调用 ppf 方法来评估近似的 PPF

>>> rng.ppf(0.975)
1.9599639857012559
>>> norm.ppf(0.975)
1.959963984540054

由于正态分布的 PPF 可作为特殊函数使用,我们还可以检查 x 误差,即近似 PPF 和精确 PPF 之间的差异

>>> import matplotlib.pyplot as plt
>>> u = np.linspace(0.01, 0.99, 1000)
>>> approxppf = rng.ppf(u)
>>> exactppf = norm.ppf(u)
>>> error = np.abs(exactppf - approxppf)
>>> plt.plot(u, error)
>>> plt.xlabel('u')
>>> plt.ylabel('error')
>>> plt.title('Error between exact and approximated PPF (x-error)')
>>> plt.show()
../../_images/scipy-stats-sampling-NumericalInversePolynomial-1_01_00.png
属性:
intervals

获取计算中使用的区间数。

方法

cdf(x)

给定分布的近似累积分布函数。

ppf(u)

给定分布的近似 PPF。

qrvs([size, d, qmc_engine])

给定 RV 的准随机变量。

rvs([size, random_state])

从分布中采样。

set_random_state([random_state])

设置底层均匀随机数生成器。

u_error([sample_size])

使用蒙特卡罗模拟估计近似的 u 误差。