如果我理解正确,scipy.stats
离散分布的cdf
应该返回给定参数值的概率之和。
因此,scipy.stats.binom(7000000000, 0.5).cdf(6999999999)
应该返回几乎正好为1的值,因为在70亿次试验中,有50/50的机会,在70亿减去1或更少的试验中成功的概率是非常确定的。相反,我得到了np.nan
。事实上,对于提供给.cdf
的任何值,除了70亿本身(或更多(,我都会得到np.nan
。
这是怎么回事?scipy.stats
发行版可以处理的数量是否有文档中没有的限制?
TL;DR
内部计算过程中缺乏浮点精度。尽管scipy是一个Python库,但它的核心是用C编写的,并使用C数字类型。
让我给你看一个例子:
import scipy.stats
for i in range (13):
trials = 10 ** i
print(f"i: {i}tprobability: {scipy.stats.binom(trials, 0.5).cdf(trials - 1)}")
输出为:
i: 0 probability: 0.5
i: 1 probability: 0.9990234375
i: 2 probability: 0.9999999999999999
i: 3 probability: 0.9999999999999999
i: 4 probability: 0.9999999999999999
i: 5 probability: 0.9999999999999999
i: 6 probability: 0.9999999999999999
i: 7 probability: 0.9999999999999999
i: 8 probability: 0.9999999999999999
i: 9 probability: 0.9999999999999999
i: 10 probability: nan
i: 11 probability: nan
i: 12 probability: nan
原因在于二项分布的CDF公式(我不能嵌入图像,所以这里有wiki的链接:https://en.wikipedia.org/wiki/Binomial_distribution
在scipy源代码中,我们可以看到对该实现的引用:http://www.netlib.org/cephes/doubldoc.html#bdtr
在它的深处,它涉及到trials
的划分(这里incbet.c, line 375: ai = 1.0 / a;
被称为a
,但nwm(。如果你的trials
太大,这个除法的结果就太小了,以至于当我们把这个小数字加到另一个不是那么小的数字上时,它实际上不会改变,因为我们这里缺乏浮点精度(到目前为止只有64位(。然后,经过更多的算术运算,我们试图从一个数字中得到对数,但它等于零,因为它在应该改变的时候没有改变。并且没有定义log(0)
,这等于np.nan
。