scipy.stats中cdf的精度

2 投票
2 回答
4312 浏览
提问于 2025-04-16 19:16

我正在使用卡方分布作为一个理论问题来进行模拟系统的研究。

对于一个给定的区间,我需要估算这个分布的概率质量函数(PMF),这个函数是区间内概率密度函数(PDF)积分的结果。这个值应该接近于区间中心的PDF值,但根据PDF的形状,可能会有一些小的差别。

这是我所做的:

import numpy
from scipy.stats import chi2

dist = chi2(10)
nbins = 120

F = dist.cdf(numpy.arange(nbins+1))
pmf = F[1:] - F[:-1] # surface inside the interval
pmf /= pmf.sum() # Normalisation

问题是,调用chi2.cdf(100, 10)及以上的值会得到1.0。所以我能得到的最小值大约是1.11e-16。但是,chi2.pdf(100, 10)并不完全是0(大约是2.5e-17)。

我的问题是:我怎样才能更精确地得到我的PMF估算(也许可以达到1e-25)?为什么CDF函数的精度比PDF函数低?

2 个回答

7

通常每当我遇到精度问题时,我首先会使用mpmath这个工具。90%的情况下,它都能很好地解决问题,而且速度也很快。在这个例子中,我们可以这样写:

import mpmath
mpmath.mp.dps = 50 # decimal digits of precision

def pdf(x,k):
    x,k = mpmath.mpf(x), mpmath.mpf(k)
    if x < 0: return 0
    return 1/(2**(k/2) * mpmath.gamma(k/2)) * (x**(k/2-1)) * mpmath.exp(-x/2)

def cdf(x,k): 
    x,k = mpmath.mpf(x), mpmath.mpf(k) 
    return mpmath.gammainc(k/2, 0, x/2, regularized=True)

def cdf_via_quad(s,k):
    return mpmath.quad(lambda x: pdf(x,k), [0, s])

使用你的F函数:

>>> pdf(2,10)
mpf('0.0076641550244050483665734118783637680717877318964951605')
>>> cdf(2,10)
mpf('0.003659846827343712345456455812710150667594853455628779')
>>> cdf_via_quad(2,10)
mpf('0.003659846827343712345456455812710150667594853455628779')
>>> F[2]
0.0036598468273437131
>>> pdf(100,10)
mpf('2.5113930312030179466371651256862142900427508479560716e-17')
>>> cdf(100,10)
mpf('0.99999999999999994550298017079470664906667698474760744')
>>> cdf_via_quad(100,10)
mpf('0.99999999999999994550298017079470664906667698474760744')
>>> F[100]
1.0

应该很简单就能用quad来获取你需要的任何归一化。

8

cdf的值在浮点数精度上等于1,但sf的值接近于0,所以很小的差别,比如1e-20,并不会被那个大的1所掩盖。(可以参考JABS文献)

>>> probs_from_cdf = np.diff(stats.chi2.cdf(np.arange(nbins+1), 10))
>>> probs_from_sf = np.diff(stats.chi2.sf(np.arange(nbins+1)[::-1], 10))[::-1]
>>> probs_from_sf[:4]
array([ 0.00017212,  0.00348773,  0.01491609,  0.03407708])
>>> probs_from_cdf[:4]
array([ 0.00017212,  0.00348773,  0.01491609,  0.03407708])
>>> probs_from_cdf[-5:]
array([ 0.,  0.,  0.,  0.,  0.])
>>> probs_from_sf[-5:]
array([  1.94252577e-20,   1.21955220e-20,   7.65430774e-21,
         4.80270079e-21,   3.01259913e-21])

我不知道sf的准确范围有多大,也就是scipy.special.chdtrc(df, x)的准确性范围。

撰写回答