计算非常低的pvalues Python

时间:2017-06-19 18:55:09

标签: python scipy

我已经在我的代码中达到了一个点,我迭代地计算了某些条件的p值:

from scipy.stats import hypergeom
pval = min(hypergeom.sf(k, M, n, N) + hypergeom.pmf(k, M, n, N), 1)

此方法适用于“小”n(流行音乐中成功元素的数量)。我尝到了500.

在我尝试使用n=5000之后,我得到一个精度错误,因为计算出的pvalue非常低并且四舍五入为0.

如何在Python中克服这些precision errors

1 个答案:

答案 0 :(得分:4)

要计算的值小于可使用64位浮点值表示的值。您在评论中提供的一个示例是k = 5007, M = 45956, n = 18969, N = 5267。对于MnN的值,当k参数为3478时,PMF下溢为0:

In [46]: k = 5007

In [47]: M = 45956

In [48]: n = 18969

In [49]: N = 5267

In [50]: hypergeom.pmf(3476, M, n, N)
Out[50]: 9.8813129168249309e-324

In [51]: hypergeom.pmf(3477, M, n, N)
Out[51]: 4.9406564584124654e-324

In [52]: hypergeom.pmf(3478, M, n, N)
Out[52]: 0.0

解决该问题的标准方法是使用概率的对数。 scipy离散分布具有logpmflogsf函数:

In [53]: hypergeom.logpmf(3476, M, n, N)
Out[53]: -743.80749253381509

In [54]: hypergeom.logpmf(3477, M, n, N)
Out[54]: -744.95722489454783

In [55]: hypergeom.logpmf(3478, M, n, N)
Out[55]: -746.10790755529888

In [56]: hypergeom.logpmf(5007, M, n, N)
Out[56]: -3952.1782915849763

要计算hypergeom.sf(k, M, n, N) + hypergeom.pmf(k, M, n, N),您可以使用numpy.logaddexp

In [58]: np.logaddexp(hypergeom.logsf(k, M, n, N), hypergeom.logpmf(k, M, n, N))
Out[58]: -3952.1508002445375

唯一的不便是进一步的计算和比较必须基于概率的对数。如果这对您不起作用,则必须切换到提供更高精度浮点计算的库,例如mpmath。例如,以下函数使用mpmath来计算PMF和生存函数:

def hypergeom_pmf(k, M, n, N):
    tot, good = M, n
    bad = tot - good
    pmf = (mpmath.beta(good+1, 1) * mpmath.beta(bad+1,1) * mpmath.beta(tot-N+1, N+1) /
           (mpmath.beta(k+1, good-k+1) * mpmath.beta(N-k+1,bad-N+k+1) * mpmath.beta(tot+1, 1)))
    return pmf

def hypergeom_sf(k, M, n, N):
    sf = (mpmath.binomial(N, k+1) * mpmath.binomial(M-N, n - k - 1) / mpmath.binomial(M, n) *
          mpmath.hyp3f2(1, k + 1 - n, k + 1 - N, k + 2, M + k + 2 - n - N, 1))
    return sf

hypergeom_pmf(k, M, n, N)中使用的表达式取自scipy.stats.hypergeom._logpmf中的scipy实现。hypergeom_sf使用the wikipedia page on the hypergeometric distribution上给出的CDF公式。它不一定是最好的实施生存功能。)

例如:

In [107]: import mpmath

In [108]: mpmath.mp.dps = 40

In [109]: k, M, n, N
Out[109]: (5007, 45956, 18969, 5267)

In [110]: hypergeom_pmf(k, M, n, N)
Out[110]: mpf('3.897413335837289136238051958307757561884655e-1717')

In [111]: hypergeom_sf(k, M, n, N)
Out[111]: mpf('1.086314878026431217760059547783856962636701e-1718')
相关问题