Python中的二项式测试,用于非常大的数字

时间:2010-06-16 18:38:08

标签: python binomial-coefficients

我需要在Python中进行二项式测试,以便计算大约10000的'n'个数。

我已经使用scipy.misc.comb实现了一个快速的binomial_test函数,但是,它在n = 1000附近非常有限,我想因为它在计算阶乘或组合本身时达到了最大可表示的数字。这是我的功能:

from scipy.misc import comb
def binomial_test(n, k):
    """Calculate binomial probability
    """
    p = comb(n, k) * 0.5**k * 0.5**(n-k)
    return p

我如何使用本机python(或numpy,scipy ...)函数来计算二项式概率?如果可能的话,我需要scipy 0.7.2兼容代码。

非常感谢!

6 个答案:

答案 0 :(得分:9)

编辑添加此评论:请注意,正如Daniel Stutzbach所提到的,“二项式测试”可能不是原始海报所要求的(虽然他确实使用了这个表达式)。他似乎要求二项分布的概率密度函数,这不是我在下面建议的。

你试过scipy.stats.binom_test吗?

rbp@apfelstrudel ~$ python
Python 2.6.2 (r262:71600, Apr 16 2009, 09:17:39) 
[GCC 4.0.1 (Apple Computer, Inc. build 5250)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from scipy import stats
>>> print stats.binom_test.__doc__

    Perform a test that the probability of success is p.

    This is an exact, two-sided test of the null hypothesis
    that the probability of success in a Bernoulli experiment
    is `p`.

    Parameters
    ----------
    x : integer or array_like
        the number of successes, or if x has length 2, it is the
        number of successes and the number of failures.
    n : integer
        the number of trials.  This is ignored if x gives both the
        number of successes and failures
    p : float, optional
        The hypothesized probability of success.  0 <= p <= 1. The
        default value is p = 0.5

    Returns
    -------
    p-value : float
        The p-value of the hypothesis test

    References
    ----------
    .. [1] http://en.wikipedia.org/wiki/Binomial_test


>>> stats.binom_test(500, 10000)
4.9406564584124654e-324

小编辑以添加文档链接:http://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.binom_test.html#scipy.stats.binom_test

BTW:适用于scipy 0.7.2,以及当前的0.8 dev。

答案 1 :(得分:6)

任何看起来像comb(n, k) * 0.5**k * 0.5**(n-k)的解决方案都不适用于大型n。在大多数(所有?)平台上,Python float可以存储的最小值大约为2 ** - 1022。对于较大的n-k或较大的k,右侧将四舍五入为0.同样,comb(n,k)可能会变得如此之大,以至于它不适合浮动。

更强大的方法是将probability density function计算为cumulative distribution function中两个连续点之间的差异,这可以使用正则化的不完全beta函数计算(查看SciPy的“特殊函数”包)。在数学上:

pdf(p, n, k) = cdf(p, n, k) - cdf(p, n, k-1)

另一种选择是使用Normal approximation,这对于大n非常准确。如果速度是一个问题,这可能是要走的路:

from math import *

def normal_pdf(x, m, v):
    return 1.0/sqrt(2*pi*v) * exp(-(x-m)**2/(2*v))

def binomial_pdf(p, n, k):
    if n < 100:
        return comb(n, k) * p**k * p**(n-k)  # Fall back to your current method
    return normal_pdf(k, n*p, n*p*(1.0-p))

我还没有测试过代码,但这应该会给你一般的想法。

答案 2 :(得分:3)

GMPY还支持扩展精度浮点计算。例如:

>>> from gmpy import *
>>>
>>> def f(n,k,p,prec=256):
...     return mpf(comb(n,k),prec) * mpf(p,prec)**k * mpf(1-p,prec)**(n-k)
...
>>> print(f(1000,500,0.5))
0.0252250181783608019068416887621024545529410193921696384762532089115753731615931
>>>

我指定了256位的浮点精度。顺便说一句,源伪造版本已经过时了。当前版本在code.google.com上维护,并支持Python 3.x. (免责声明:我是gmpy目前的维护者。)

casevh

答案 3 :(得分:1)

我会调查GNU Multi-Precision package(gmpy),它允许你执行任意精度计算:你可能会这样做:

comb(n, k, exact=1)/2**k/2**(n-k)

但是gmpy的长整数。

实际上,如果使用精确整数计算,则组合部分可轻松达到n = 10000 ;为此,您必须使用:

comb(n, k, exact=1)

而不是溢出的浮点近似值comb(n, k)

但是,正如原始海报所指出的那样,返回的(长整数)可能太长而不能乘以浮点数!

此外,很快就会遇到另一个问题:0.5**1000 = 9.3 ... e-302已经非常接近浮动下溢......

总结:如果您确实需要k的所有n~10,000的精确结果,则需要使用与原始帖子中的公式不同的方法,该方法受到双精度浮动的限制点算术。如上所述使用gmpy可能是一个解决方案(未经测试!)。

答案 4 :(得分:0)

不是特别是Python解决方案,但如果你可以处理小的小数错误,你可以尝试使用Stirling的近似n!:

comb(n,k)= n!/(k!*(n-k)!),其中n!对于大n来说,大约是sqrt(2 * Pi n)(n / e)^ n。

对于n> 1000,分数误差应该非常小。

对于大n的概率计算,使用对数表示中间结果:

log p = log(comb(n,k)) - n * log(2)

p = exp(log(p))

答案 5 :(得分:-1)

#  This imports the array function form numpy

from numpy import array

    # the following defines the factorial function to be used in the binomial commands/
# n+1 is used in the range to include the nth term

def factorial (n):
    f=1
    for x in range(1,n+1):
        f=f*(x)
    return f

# The follwong calculates the binomial coefficients for given values of n & k 
def binomial (n,k):
    b=1
    b=(factorial(n)/(factorial(k)*factorial(n-k)))
    return int(b)

# the following lines define the pascal triangle , and print it out for 20 rows./
# in order to include nth term, the n +1 term needs to be in the range. The commands/
# append the next binomial coeficiant to a raw first and then append rows to the triangle/
# and prints a 20 row size pascal triangle
def pascal(T):
    triangle=[]
    for n in range(T):
        r=[]
        for k in range(n+1):
            r.append(binomial(n,k))
        triangle.append(r)
    return triangle

for r in pascal(20):
    print((r))