Биномиальный тест в Python для очень больших чисел

Мне нужно сделать биномиальный тест в Python, который позволяет вычислять числа " n " порядка 10000.

я реализовал быструю функцию binomial_test с помощью scipy.разное.расческа, однако, в значительной степени ограничена вокруг 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 ответов


отредактировано, чтобы добавить этот комментарий: обратите внимание, что, как упоминает Даниэль Стуцбах, "биномиальный тест", вероятно, не то, что просил оригинальный плакат (хотя он использовал это выражение). Кажется, он спрашивает о функции плотности вероятности биномиального распределения, что не то, что я предлагаю ниже.

вы пробовали scipy.статистика.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.


любое решение, которое выглядит как comb(n, k) * 0.5**k * 0.5**(n-k) не будет работать для больших n. На большинстве (всех?) платформы, наименьшее значение, которое может хранить Python float, составляет около 2* * -1022. Для больших n-k или большие k, правая сторона будет округлена до 0. Аналогично, расческа (n, k) может расти настолько большой, что она не поместится в поплавок.

более надежным подходом является вычисление функция плотности вероятности как разница между двумя последовательными точками в интегральная функция распределения, который может быть вычислен с использованием регуляризованной неполной бета-функции (посмотрите в пакете "специальные функции" SciPy). Математически:

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

другой вариант-использовать нормальной аппроксимации, что довольно точно для больших 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))

Я не проверял код, но это должно дать вам общее представление.


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 бит. Кстати, версия source forge устарела. Текущая версия поддерживается на code.google.com и поддерживает Python 3.х. (Оговорка: я нынешний хранитель gmpy.)

casevh


Я бы заглянул в 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, вам нужно использовать другой подход, чем формула из исходного поста, который страдает от ограничений double точность арифметики с плавающей запятой. Использование gmpy, как указано выше, может быть решением (не проверено!).


не специально решение Python, но если вы можете иметь дело с небольшими дробными ошибками, вы можете попробовать использовать приближение Стирлинга для n!:

расческа (n, k) = n!/(k! * (n-k)!), где N! приблизительно sqrt (2*Pin)(n/e)^n для большого n.

для n>1000 дробные ошибки должны быть очень малыми.

для расчета вероятности с большим n используйте логарифмы для промежуточных результатов:

log p = log(гребень (n, k)) - n * log (2)

p = exp(log (p))


#  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))