Биномиальный тест в 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))