【问题标题】:Confidence interval of probability prediction from logistic regression statsmodels逻辑回归统计模型概率预测的置信区间
【发布时间】:2018-05-05 00:28:22
【问题描述】:

我正在尝试从 统计学习简介 中重新创建一个图,但我无法弄清楚如何计算概率预测的置信区间。具体来说,我正在尝试重新创建该图的右侧面板 (figure 7.1),它根据年龄的 4 次多项式和相关的 95% 置信区间来预测工资>250 的概率。如果有人关心,工资数据是here

我可以使用以下代码很好地预测和绘制预测概率

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)

但我不知道如何计算预测概率的置信区间。我曾多次考虑引导数据以获取每个年龄的概率分布,但我知道有一种更简单的方法,但我无法掌握。

我有估计的系数协方差矩阵和与每个估计系数相关的标准误差。鉴于此信息,我将如何计算上图右侧面板中所示的置信区间?

谢谢!

【问题讨论】:

    标签: python logistic-regression statsmodels confidence-interval


    【解决方案1】:

    您可以使用delta method 找到预测概率的近似方差。即,

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

    其中gradient是模型系数预测概率的导数向量,cov是系数的协方差矩阵。

    Delta 方法被证明对所有最大似然估计都渐近地起作用。但是,如果您的训练样本较小,渐近方法可能效果不佳,您应该考虑自举。

    这是一个将 delta 方法应用于逻辑回归的玩具示例:

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

    它绘制了以下漂亮的图片:

    对于您的示例,代码将是

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

    它会给出以下图片

    看起来很像一条蟒蛇,里面有一头大象。

    您可以将其与 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()
    

    delta 方法和 bootstrap 的结果看起来几乎一样。

    然而,本书的作者走的是第三种方式。他们使用的事实是

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

    并计算线性部分的置信区间,然后用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()
    

    所以他们得到了发散区间:

    这些方法产生如此不同的结果,因为它们假设不同的事物(预测概率和对数赔率)正态分布。即,delta 方法假设预测概率是正常的,而在书中,log-odds 是正常的。事实上,它们在有限样本中都不是正态的,在无限样本中它们都收敛到正态,但它们的方差同时收敛到零。最大似然估计对重新参数化不敏感,但它们的估计分布是,这就是问题所在。

    【讨论】:

    • 优秀的回答大卫,谢谢!不同的置信区间真的让我大吃一惊。
    • @DavidDale 不错的答案,但如果您澄清哪种方法假设预测概率为正态分布(增量方法)以及哪种方法假设对数概率为正态分布( “转换”方法,即您显示的最后一个图)。
    • 嗨,大卫,很好的答案 - 我试图用 Sklearn.LogisticRegression 重现你的结果,但 predict_proba 的结果不同 - 为什么你会这样认为?
    • 嗨,大卫,您使用线性部分的置信区间计算的结果将为我们提供响应的预测区间?或平均响应的置信区间?如果给出置信区间,我们如何计算预测区间?
    • 我计算平均响应的置信区间。它是二分类,所以预测区间总是{0}、{1}或[0, 1]。我认为这样的间隔没有多大意义。
    【解决方案2】:

    这是一种指导性且有效的方法,用于在 statsmodels Logit().fit() 对象( 'fit'),与 ISLR 书中的方法和 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
    

    A figure similar to the one in the book ISLR

    阴影区域表示拟合和单个观测值的 95% 置信区间。

    欢迎提出改进意见。

    【讨论】:

      猜你喜欢
      • 2013-01-03
      • 2023-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-07-05
      • 2021-10-30
      相关资源
      最近更新 更多