Вычисление доверительных интервалов для лямбда-параметра экспоненциального распределения в Python - PullRequest
0 голосов
/ 06 августа 2020

Предположим, у меня есть образец, который, как мне кажется, следует экспоненциальному распределению. Я хочу оценить параметр распределения (лямбда) и некоторые показатели уверенности. Подойдет либо доверительный интервал, либо стандартная ошибка. К сожалению, scipy.stats.expon.fit, похоже, не допускает этого. Вот пример, я буду использовать лямбда = 1/120 = 0,008333 для тестовых данных:

""" Generate test data"""
import scipy.stats
test_data = scipy.stats.expon(scale=120).rvs(size=3000)

""" Scipy.stats fit"""
fit = scipy.stats.expon.fit(test_data)
print("\nExponential parameters:", fit, " Specifically lambda: ", 1/fit[1], "\n")

# Exponential parameters: (0.0066790678905608875, 116.8376079908356)  Specifically lambda:  0.008558887991599736 

Ответ на аналогичный вопрос о данных с гамма-распределением предлагает использовать GenericLikelihoodModel из модуля statsmodels. Хотя я могу подтвердить, что это хорошо работает для данных с гамма-распределением, это не работает для экспоненциальных распределений, потому что оптимизация, очевидно, приводит к необратимой матрице Гессе. Это происходит либо из-за не конечных элементов в гессиане, либо из np.linalg.eigh, производящих неположительные собственные значения для гессиана. ( Исходный код здесь; HessianInversionWarning вызывается в методе fit класса LikelihoodModel. ).

""" Statsmodel fit"""
from statsmodels.base.model import GenericLikelihoodModel

class Expon(GenericLikelihoodModel):
    nparams = 2
    def loglike(self, params):
        return scipy.stats.expon.logpdf(self.endog, *params).sum()


res = Expon(test_data).fit(start_params=fit)
res.df_model = len(fit)
res.df_resid = len(test_data) - len(fit)
print(res.summary())

#Optimization terminated successfully.
#         Current function value: 5.760785
#         Iterations: 38
#         Function evaluations: 76
#/usr/lib/python3.8/site-packages/statsmodels/tools/numdiff.py:352: RuntimeWarning: invalid value encountered in double_scalars
#  hess[i, j] = (f(*((x + ee[i, :] + ee[j, :],) + args), **kwargs)
#/usr/lib/python3.8/site-packages/statsmodels/base/model.py:547: HessianInversionWarning: Inverting hessian failed, no bse or cov_params available
#  warn('Inverting hessian failed, no bse or cov_params '
#/usr/lib/python3.8/site-packages/scipy/stats/_distn_infrastructure.py:903: RuntimeWarning: invalid value encountered in greater
#  return (a < x) & (x < b)
#/usr/lib/python3.8/site-packages/scipy/stats/_distn_infrastructure.py:903: RuntimeWarning: invalid value encountered in less
#  return (a < x) & (x < b)
#/usr/lib/python3.8/site-packages/scipy/stats/_distn_infrastructure.py:1912: RuntimeWarning: invalid value encountered in less_equal
#  cond2 = cond0 & (x <= _a)
#                                Expon Results                                 
#==============================================================================
#Dep. Variable:                      y   Log-Likelihood:                -17282.
#Model:                          Expon   AIC:                         3.457e+04
#Method:            Maximum Likelihood   BIC:                         3.459e+04
#Date:                Thu, 06 Aug 2020                                         
#Time:                        13:55:24                                         
#No. Observations:                3000                                         
#Df Residuals:                    2998                                         
#Df Model:                           2                                         
#==============================================================================
#                 coef    std err          z      P>|z|      [0.025      0.975]
#------------------------------------------------------------------------------
#par0           0.0067        nan        nan        nan         nan         nan
#par1         116.8376        nan        nan        nan         nan         nan
#==============================================================================

Кажется, это происходит каждый раз, поэтому может быть что-то связанное с экспоненциально распределенными данными.

Есть ли другой возможный подход? Или я что-то упускаю или делаю здесь что-то не так? вместо

test_data = scipy.stats.expon(scale=120).rvs(size=3000)

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

В результате два других варианта, которые я рассмотрел ( вручную вычисляя интервалы соответствия и доверительные интервалы, следуя стандартной процедуре, описанной в wikipedia ), и используя scikits.bootstrap как , предложенное в этом ответе действительно работает и является частью решения, которое я добавлю через минуту и ​​не проблема.

1 Ответ

0 голосов
/ 06 августа 2020

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

Остается только то, что scipy.stats.expon.fit не предлагает возможность вычисления достоверности или ошибок, а также использование GenericLikelihoodModel из модуля statsmodels в качестве , предложенного здесь , не выполняется из-за искаженного гессиана.

Однако есть три подхода, которые действительно работают:

1. Использование простой процедуры вывода для доверительных интервалов в экспоненциальных данных, как указано в статье википедии

""" Maximum likelihood"""
import numpy as np
ML_lambda = 1 / np.mean(test_data)
print("\nML lambda: {0:8f}".format(ML_lambda))

#ML lambda: 0.008558

""" Bias corrected ML"""
ML_BC_lambda = ML_lambda - ML_lambda / (len(test_data) - 1)
print("\nML bias-corrected lambda: {0:8f}".format(ML_BC_lambda))

#ML bias-corrected lambda: 0.008556

Расчет доверительных интервалов ::

""" Maximum likelihood 95% confidence"""
CI_distance = ML_BC_lambda * 1.96/(len(test_data)**0.5)
print("\nLambda with confidence intervals: {0:8f} +/- {1:8f}".format(ML_BC_lambda, CI_distance))
print("Confidence intervals: ({0:8f}, {1:9f})".format(ML_BC_lambda - CI_distance, ML_BC_lambda + CI_distance))

#Lambda with confidence intervals: 0.008556 +/- 0.000306
#Confidence intervals: (0.008249,  0.008862)

Второй вариант с этим: Кроме того, уравнение доверительного интервала также должно быть действительным для лямбда-оценки, полученной другим, например, из scipy.stats.expon.fit. (Я думал, что процедура подгонки в scipy.stats.expon.fit более надежна, но оказалось, что она на самом деле такая же, без коррекции смещения (см. Выше).)

""" Maximum likelihood 95% confidence based on scipy.stats fit"""
scipy_stats_lambda = 1 / fit[1]
scipy_stats_CI_distance = scipy_stats_lambda * 1.96/(len(test_data)**0.5)
print("\nOr, based on scipy.stats fit:")
print("Lambda with confidence intervals: {0:8f} +/- {1:8f}".format(scipy_stats_lambda, scipy_stats_CI_distance))
print("Confidence intervals: ({0:8f}, {1:9f})".format(scipy_stats_lambda - scipy_stats_CI_distance, 
                                                                scipy_stats_lambda + scipy_stats_CI_distance))

#Or, based on scipy.stats fit:
#Lambda with confidence intervals: 0.008559 +/- 0.000306
#Confidence intervals: (0.008253,  0.008865)

2. Загрузка с scikits.bootstrap после предложения в этом ответе Это дает InstabilityWarning: Some values were NaN; results are probably unstable (all values were probably equal), поэтому к этому следует относиться немного скептически.

""" Bootstrapping with scikits"""
print("\n")
import scikits.bootstrap as boot
bootstrap_result = boot.ci(test_data, scipy.stats.expon.fit)
print(bootstrap_result)

#tmp/expon_fit_test.py:53: InstabilityWarning: Some values were NaN; results are probably unstable (all values were probably equal)
#  bootstrap_result = boot.ci(test_data, scipy.stats.expon.fit)
#[[6.67906789e-03 1.12615588e+02]
# [6.67906789e-03 1.21127091e+02]]

3. Используя rpy2

""" Using r modules with rpy2"""
import rpy2.robjects as robjects
from rpy2.robjects.packages import importr
MASS = importr('MASS')
import rpy2.robjects.numpy2ri
rpy2.robjects.numpy2ri.activate()
rpy_fit = MASS.fitdistr(test_data, "exponential")
rpy_estimate = rpy_fit.rx("estimate")[0][0]
rpy_sd = rpy_fit.rx("sd")[0][0]
rpy_lower = rpy_estimate - 2*rpy_sd
rpy_upper = rpy_estimate + 2*rpy_sd
print("\nrpy2 fit: \nLambda={0:8f} +/- {1:8f}, CI: ({2:8f}, {3:8f})".format(rpy_estimate, rpy_sd, rpy_lower, rpy_upper))

#rpy2 fit: 
#Lambda=0.008558 +/- 0.000156, CI: (0.008246, 0.008871)
...