Точность cdf в scipy.stats

1

Я использую chi2 как теоретическую проблему для системы моделирования.

Для данного интервала мне нужно оценить это распределение как PMF, определяемое как интеграл PDF внутри этого интервала. Это значение должно быть близко к значению PDF в центре интервала, но может немного отличаться в зависимости от формы PDF.

Вот что я делаю:

import numpy
from scipy.stats import chi2

dist = chi2(10)
nbins = 120

F = dist.cdf(numpy.arange(nbins+1))
pmf = F[1:] - F[:-1] # surface inside the interval
pmf /= pmf.sum() # Normalisation

Проблема заключается в том, что chi2.cdf(100, 10) и выше дает ровно 1,0. Таким образом, минимальное значение, которое я могу получить, составляет около 1. 11e- 16. Но chi2.pdf(100, 10) не точно 0 (это около 2. 5e- 17).

Мой вопрос: как я могу получить оценку pmf с большей точностью (возможно, до 1e- 25)? Почему функция cdf менее точная, чем функция pdf?

Теги:
numpy
scipy
precision
cdf

2 ответа

6
Лучший ответ

cdf находится в точности с плавающей запятой, равной одной, но sf близок к нулю, поэтому крошечные отличия 1e- 20 не покрываются большим 1. (см. ссылку JABS)

>>> probs_from_cdf = np.diff(stats.chi2.cdf(np.arange(nbins+1), 10))
>>> probs_from_sf = np.diff(stats.chi2.sf(np.arange(nbins+1)[::-1], 10))[::-1]
>>> probs_from_sf[:4]
array([ 0.00017212,  0.00348773,  0.01491609,  0.03407708])
>>> probs_from_cdf[:4]
array([ 0.00017212,  0.00348773,  0.01491609,  0.03407708])
>>> probs_from_cdf[-5:]
array([ 0.,  0.,  0.,  0.,  0.])
>>> probs_from_sf[-5:]
array([  1.94252577e-20,   1.21955220e-20,   7.65430774e-21,
         4.80270079e-21,   3.01259913e-21])

Я не знаю, насколько далеко находится точный диапазон sf, то есть scipy.special.chdtrc(df, x), идет

  • 0
    Спасибо! Это именно то, что я искал. И в качестве бонуса вы показали мне функцию сравнения, о которой я не знал.
5

Обычно, когда у меня есть проблема с точностью, первым инструментом, к которому я обращаюсь, является mpmath. В 90% случаев это просто работает (тм), достаточно быстро. В этом случае мы можем написать:

import mpmath
mpmath.mp.dps = 50 # decimal digits of precision

def pdf(x,k):
    x,k = mpmath.mpf(x), mpmath.mpf(k)
    if x < 0: return 0
    return 1/(2**(k/2) * mpmath.gamma(k/2)) * (x**(k/2-1)) * mpmath.exp(-x/2)

def cdf(x,k): 
    x,k = mpmath.mpf(x), mpmath.mpf(k) 
    return mpmath.gammainc(k/2, 0, x/2, regularized=True)

def cdf_via_quad(s,k):
    return mpmath.quad(lambda x: pdf(x,k), [0, s])

давая (используя ваш F):

>>> pdf(2,10)
mpf('0.0076641550244050483665734118783637680717877318964951605')
>>> cdf(2,10)
mpf('0.003659846827343712345456455812710150667594853455628779')
>>> cdf_via_quad(2,10)
mpf('0.003659846827343712345456455812710150667594853455628779')
>>> F[2]
0.0036598468273437131
>>> pdf(100,10)
mpf('2.5113930312030179466371651256862142900427508479560716e-17')
>>> cdf(100,10)
mpf('0.99999999999999994550298017079470664906667698474760744')
>>> cdf_via_quad(100,10)
mpf('0.99999999999999994550298017079470664906667698474760744')
>>> F[100]
1.0

Должно быть просто использовать квад, чтобы получить любую нормализацию, в которой вы нуждаетесь.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню