web-dev-qa-db-fra.com

Intervalle de confiance de la prévision des probabilités à partir des modèles de statistiques de régression logistique

J'essaie de recréer un graphique à partir de Une introduction à l'apprentissage statistique et j'ai du mal à comprendre comment calculer l'intervalle de confiance pour une prédiction de probabilité . Plus précisément, j'essaie de recréer le panneau de droite de cette figure ( figure 7.1 ) qui prédit la probabilité que le salaire> 250 basé sur un polynôme de degré 4 avec des intervalles de confiance à 95% associés . Les données sur les salaires sont ici si quelqu'un s'en soucie.

Je peux bien prédire et tracer les probabilités prédites avec le code suivant

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import statsmodels.api as sm
from sklearn.preprocessing import PolynomialFeatures

wage = pd.read_csv('../../data/Wage.csv', index_col=0)
wage['wage250'] = 0
wage.loc[wage['wage'] > 250, 'wage250'] = 1

poly = Polynomialfeatures(degree=4)
age = poly.fit_transform(wage['age'].values.reshape(-1, 1))

logit = sm.Logit(wage['wage250'], age).fit()

age_range_poly = poly.fit_transform(np.arange(18, 81).reshape(-1, 1))

y_proba = logit.predict(age_range_poly)

plt.plot(age_range_poly[:, 1], y_proba)

Mais je ne sais pas comment les intervalles de confiance des probabilités prédites sont calculés. J'ai pensé à amorcer les données plusieurs fois pour obtenir la distribution des probabilités pour chaque âge, mais je sais qu'il existe un moyen plus simple qui est juste hors de ma portée.

J'ai la matrice de covariance des coefficients estimés et les erreurs standard associées à chaque coefficient estimé. Comment pourrais-je procéder pour calculer les intervalles de confiance comme indiqué dans le panneau de droite de la figure ci-dessus, compte tenu de ces informations?

Merci!

11
Taylor

Vous pouvez utiliser méthode delta pour trouver la variance approximative de la probabilité prédite. À savoir,

var(proba) = np.dot(np.dot(gradient.T, cov), gradient)

gradient est le vecteur des dérivées de la probabilité prédite par les coefficients du modèle, et cov est la matrice de covariance des coefficients.

Il est prouvé que la méthode Delta fonctionne de manière asymptotique pour toutes les estimations du maximum de vraisemblance. Cependant, si vous disposez d'un petit échantillon d'apprentissage, les méthodes asymptotiques peuvent ne pas fonctionner correctement et vous devriez envisager l'amorçage.

Voici un exemple jouet d'application de la méthode delta à la régression logistique:

import numpy as np
import statsmodels.api as sm
import matplotlib.pyplot as plt

# generate data
np.random.seed(1)
x = np.arange(100)
y = (x * 0.5 + np.random.normal(size=100,scale=10)>30)
# estimate the model
X = sm.add_constant(x)
model = sm.Logit(y, X).fit()
proba = model.predict(X) # predicted probability

# estimate confidence interval for predicted probabilities
cov = model.cov_params()
gradient = (proba * (1 - proba) * X.T).T # matrix of gradients for each observation
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in gradient])
c = 1.96 # multiplier for confidence interval
upper = np.maximum(0, np.minimum(1, proba + std_errors * c))
lower = np.maximum(0, np.minimum(1, proba - std_errors * c))

plt.plot(x, proba)
plt.plot(x, lower, color='g')
plt.plot(x, upper, color='g')
plt.show()

Il dessine la belle image suivante: enter image description here

Pour votre exemple, le code serait

proba = logit.predict(age_range_poly)
cov = logit.cov_params()
gradient = (proba * (1 - proba) * age_range_poly.T).T 
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in gradient])
c = 1.96 
upper = np.maximum(0, np.minimum(1, proba + std_errors * c))
lower = np.maximum(0, np.minimum(1, proba - std_errors * c))

plt.plot(age_range_poly[:, 1], proba)
plt.plot(age_range_poly[:, 1], lower, color='g')
plt.plot(age_range_poly[:, 1], upper, color='g')
plt.show()

et cela donnerait l'image suivante

enter image description here

Ressemble à peu près à un boa-constrictor avec un éléphant à l'intérieur.

Vous pouvez le comparer avec les estimations bootstrap:

preds = []
for i in range(1000):
    boot_idx = np.random.choice(len(age), replace=True, size=len(age))
    model = sm.Logit(wage['wage250'].iloc[boot_idx], age[boot_idx]).fit(disp=0)
    preds.append(model.predict(age_range_poly))
p = np.array(preds)
plt.plot(age_range_poly[:, 1], np.percentile(p, 97.5, axis=0))
plt.plot(age_range_poly[:, 1], np.percentile(p, 2.5, axis=0))
plt.show()

enter image description here

Les résultats de la méthode delta et bootstrap se ressemblent à peu près.

Les auteurs du livre, cependant, suivent la troisième voie. Ils utilisent le fait que

proba = np.exp (np.dot (x, params))/(1 + np.exp (np.dot (x, params))))

et calculer l'intervalle de confiance pour la partie linéaire, puis transformer avec la fonction logit

xb = np.dot(age_range_poly, logit.params)
std_errors = np.array([np.sqrt(np.dot(np.dot(g, cov), g)) for g in age_range_poly])
upper_xb = xb + c * std_errors
lower_xb = xb - c * std_errors
upper = np.exp(upper_xb) / (1 + np.exp(upper_xb))
lower = np.exp(lower_xb) / (1 + np.exp(lower_xb))
plt.plot(age_range_poly[:, 1], upper)
plt.plot(age_range_poly[:, 1], lower)
plt.show()

Ils obtiennent donc l'intervalle divergent:

enter image description here

Ces méthodes produisent des résultats si différents car elles supposent différentes choses (probabilité prédite et log-odds) distribuées normalement. À savoir, la méthode delta suppose que les probabilités prédites sont normales, et dans le livre, les log-odds sont normaux. En fait, aucun d'entre eux n'est normal dans les échantillons finis, mais ils convergent tous vers des échantillons infinis, mais leurs variances convergent vers zéro en même temps. Les estimations du maximum de vraisemblance sont insensibles à la reparamétrisation, mais leur distribution estimée l'est, et c'est le problème.

17
David Dale

Voici une méthode instructive et efficace pour calculer les erreurs standard ('se') de l'ajustement ('mean_se') et les observations uniques ('obs_se') au-dessus d'un modèle Logit (). Fit (). Fit () object ('fit') ), identique à la méthode du livre ISLR et à la dernière méthode de la réponse de David Dale:

fit_mean = fit.model.exog.dot(fit.params)
fit_mean_se = ((fit.model.exog*fit.model.exog.dot(fit.cov_params())).sum(axis=1))**0.5
fit_obs_se = ( ((fit.model.endog-fit_mean).std(ddof=fit.params.shape[0]))**2 + \
                fit_mean_se**2 )**0.5

n chiffre similaire à celui du livre ISLR

Les régions ombrées représentent les intervalles de confiance à 95% pour les observations d'ajustement et uniques.

Les idées d'amélioration sont les bienvenues.

0
Haeraeus