Logistic regression
2026-10-26
Twenty-five people succeeded or failed at a programming task.
Months of programming experience was recorded for each person.
Can we predict probability of success based on experience?
Assume \[ Y_i \sim \text{Bernoulli}(\pi_i), \quad \log\left(\frac{\pi_i}{1-\pi_i}\right) = \beta_0 + \beta_1 x_i, \] for \(i =1,\dots,n\), where
Logistic regression assumes the log-odds are linear in the predictor.
Let \(\pi\) be the probability of success.
Then \(\pi/(1-\pi)\) is called the odds in favor of success.
Newton-Raphson algorithm for computing \(\hat \beta_0\) and \(\hat \beta_1\).
glm() function in R to obtain \(\hat \beta_0\) and \(\hat \beta_1\).Use glm() function with the option family = "binomial".
Call:
glm(formula = success ~ experience, family = "binomial")
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) -3.060 1.259 -2.43 0.015 *
experience 0.162 0.065 2.49 0.013 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 34.296 on 24 degrees of freedom
Residual deviance: 25.425 on 23 degrees of freedom
AIC: 29.42
Number of Fisher Scoring iterations: 4
experience experience
0.0341 0.2888
2.5 % 97.5 %
(Intercept) -5.5280 -0.591
experience 0.0341 0.289
To test \(H_0\): \(\beta_1 = 0\) versus \(H_1\): \(\beta_1 \neq 0\), do:
Compute \(Z_{\operatorname{test}} = \dfrac{\hat \beta_1}{\widehat{\operatorname{se}}\{\hat\beta_1\}}\).
Reject \(H_0\) at \(\alpha\) if \(|Z_{\operatorname{test}}| > z_{\alpha/2}\).
Obtain p value as \(2(1- P(Z > |Z_{\operatorname{test}}|))\), \(Z \sim \text{Normal}(0,1)\).
The summary() function on the glm() output prints this p value.
Let \(\pi_0\) and \(\pi_1\) be success probs under an initial and an altered condition, respectively.
Then we call the ratio \[ \displaystyle \frac{\pi_1/(1-\pi_1)}{\pi_0/(1-\pi_0)} \] the odds ratio associated with the change from the initial to the altered condition.
The odds ratio is the factor by which the odds are multiplied when the initial condition is changed to the altered condition.
Let \(\pi_0\) and \(\pi_1\) be the “success” probabilities at \(x_0\) and \(x_0 + 1\).
Then we have the two equations
Subtracting the first equation from the second gives \[ \beta_1 = \log\left(\frac{\pi_1}{1-\pi_1}\right) - \log\left(\frac{\pi_0}{1-\pi_0}\right) = \log\left(\frac{\pi_1/(1-\pi_1)}{\pi_0/(1-\pi_0)}\right). \]
The quantity \(\dfrac{\pi_1/(1-\pi_1)}{\pi_0/(1-\pi_0)}\) is called an odds ratio.
So \(\beta_1\) is log of the odds ratio associated with a unit increase in \(x\).
From the previous slide, we have \[ e^{\beta_1} = \frac{\pi_1/(1-\pi_1)}{\pi_0/(1-\pi_0)}. \]
Can build a CI for \(e^{\beta_1}\) by exponentiating the CI for \(\beta_1\).
Gives CI for \(e^{\beta_1}\) as \([e^{\hat \beta_1 - z_{\alpha/2}\widehat{\operatorname{se}}\{\hat\beta_1\}},e^{\hat \beta_1 + z_{\alpha/2}\widehat{\operatorname{se}}\{\hat\beta_1\}}]\).
A unit increase in \(x\) multiplies the odds of success by the factor \(e^{\beta_1}\).
What if the CI for \(e^{\beta_1}\) contains \(1\)?
Each additional month of experience increases the odds of completing the programming task by a factor of 1.035 to 1.335, with \(95\%\) confidence.
The simulated envelope method is described in Kutner et al. (2005):
Asks: If the model is correct, how would the deviance residuals behave?
Response is credit rating (good/bad), various predictors.
[1] "checking_status" "duration" "credit_history"
[4] "purpose" "credit_amount" "savings_status"
[7] "employment" "installment_commitment" "personal_status"
[10] "other_parties" "residence_since" "property_magnitude"
[13] "age" "other_payment_plans" "housing"
[16] "existing_credits" "job" "num_dependents"
[19] "own_telephone" "foreign_worker" "class"
Observe \((\mathbf{x}_1,Y_1),\dots,(\mathbf{x}_n,Y_n)\), \(\mathbf{x}_i = (x_{i1},\dots,x_{ip})^T\) and assume \[ Y_i \sim \text{Bernoulli}(\pi_i), \quad \log\left(\frac{\pi_i}{1-\pi_i}\right) = \beta_0 + \beta_1 x_{i1} + \dots + \beta_p x_{ip}, \] for \(i =1,\dots,n\), where
So we assume the log-odds are a linear function of the predictors.
Call:
glm(formula = class ~ ., family = "binomial", data = credg)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 1.50e+00 1.25e+00 1.21 0.22780
checking_status>=200 9.66e-01 3.69e-01 2.62 0.00890 **
checking_status0<=X<200 3.75e-01 2.18e-01 1.72 0.08540 .
checking_statusno checking 1.71e+00 2.32e-01 7.37 1.7e-13 ***
duration -2.79e-02 9.30e-03 -3.00 0.00272 **
credit_historycritical/other existing credit 1.58e+00 4.38e-01 3.60 0.00031 ***
credit_historydelayed previously 9.97e-01 4.70e-01 2.12 0.03410 *
credit_historyexisting paid 7.29e-01 3.85e-01 1.89 0.05824 .
credit_historyno credits/all paid 1.43e-01 5.49e-01 0.26 0.79392
purposedomestic appliance -2.17e-01 8.04e-01 -0.27 0.78698
purposeeducation -7.76e-01 4.66e-01 -1.67 0.09572 .
purposefurniture/equipment 5.15e-02 3.54e-01 0.15 0.88439
purposenew car -7.40e-01 3.34e-01 -2.22 0.02667 *
purposeother 7.49e-01 8.00e-01 0.94 0.34920
purposeradio/tv 1.51e-01 3.37e-01 0.45 0.65300
purposerepairs -5.24e-01 5.93e-01 -0.88 0.37743
purposeretraining 1.32e+00 1.23e+00 1.07 0.28462
purposeused car 9.26e-01 4.41e-01 2.10 0.03564 *
credit_amount -1.28e-04 4.44e-05 -2.89 0.00389 **
savings_status>=1000 1.34e+00 5.25e-01 2.55 0.01073 *
savings_status100<=X<500 3.58e-01 2.86e-01 1.25 0.21113
savings_status500<=X<1000 3.76e-01 4.01e-01 0.94 0.34848
savings_statusno known savings 9.47e-01 2.62e-01 3.61 0.00031 ***
employment>=7 2.10e-01 2.95e-01 0.71 0.47672
employment1<=X<4 1.16e-01 2.42e-01 0.48 0.63242
employment4<=X<7 7.64e-01 3.05e-01 2.50 0.01227 *
employmentunemployed -6.69e-02 4.27e-01 -0.16 0.87548
installment_commitment -3.30e-01 8.83e-02 -3.74 0.00018 ***
personal_statusmale div/sep -2.75e-01 3.87e-01 -0.71 0.47604
personal_statusmale mar/wid 9.16e-02 3.12e-01 0.29 0.76891
personal_statusmale single 5.41e-01 2.10e-01 2.57 0.01011 *
other_partiesguarantor 1.41e+00 5.69e-01 2.49 0.01283 *
other_partiesnone 4.36e-01 4.10e-01 1.06 0.28770
residence_since -4.78e-03 8.64e-02 -0.06 0.95592
property_magnitudelife insurance -8.69e-02 2.31e-01 -0.38 0.70711
property_magnitudeno known property -5.36e-01 4.02e-01 -1.33 0.18221
property_magnitudereal estate 1.95e-01 2.36e-01 0.82 0.40974
age 1.45e-02 9.22e-03 1.58 0.11498
other_payment_plansnone 6.46e-01 2.39e-01 2.70 0.00687 **
other_payment_plansstores 1.23e-01 4.12e-01 0.30 0.76488
housingown -2.40e-01 4.50e-01 -0.53 0.59369
housingrent -6.84e-01 4.77e-01 -1.43 0.15166
existing_credits -2.72e-01 1.90e-01 -1.44 0.15111
jobskilled -7.52e-02 2.85e-01 -0.26 0.79142
jobunemp/unskilled non res 4.79e-01 6.62e-01 0.72 0.46909
jobunskilled resident -5.67e-02 3.50e-01 -0.16 0.87145
num_dependents -2.65e-01 2.49e-01 -1.06 0.28825
own_telephoneyes 3.00e-01 2.01e-01 1.49 0.13606
foreign_workeryes -1.39e+00 6.26e-01 -2.22 0.02609 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1221.73 on 999 degrees of freedom
Residual deviance: 895.82 on 951 degrees of freedom
AIC: 993.8
Number of Fisher Scoring iterations: 5
Note that glm() estimates three coefficients for checking_status.
Numeric predictors to encode the levels of the categorical predictor: \[\begin{align*} x_{i1} &= \left\{\begin{array}{ll} 1& 200 \leq \texttt{checking}\\ 0& \text{otherwise}\end{array}\right. \\ x_{i2} &= \left\{\begin{array}{ll} 1& 0 \leq \texttt{checking} <200 \\ 0& \text{otherwise}\end{array}\right.\\ x_{i3} &= \left\{\begin{array}{ll} 1& \texttt{no checking}\\ 0& \text{otherwise}\end{array}\right. \end{align*}\]
Likewise for other categorical predictors.
Test whether any level of checking status is important to the credit score.
credg_red <- credg[,-1] # remove checking_status column
glm_full <- glm(class ~ ., family = "binomial", data = credg)
glm_red <- glm(class ~ ., family = "binomial", data = credg_red)
p <- length(coef(glm_full)) - 1
s <- nlevels(credg$checking_status) - 1
1 - pchisq(glm_red$deviance - glm_full$deviance,s)[1] 2.73e-14
Call:
glm(formula = class ~ checking_status + duration + credit_history +
purpose + credit_amount + savings_status + installment_commitment +
personal_status + other_parties + age + other_payment_plans +
housing + own_telephone + foreign_worker, family = "binomial",
data = credg)
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 4.84e-01 1.02e+00 0.48 0.63436
checking_status>=200 1.02e+00 3.63e-01 2.82 0.00474 **
checking_status0<=X<200 3.90e-01 2.12e-01 1.84 0.06593 .
checking_statusno checking 1.72e+00 2.28e-01 7.53 5e-14 ***
duration -2.57e-02 8.94e-03 -2.87 0.00407 **
credit_historycritical/other existing credit 1.37e+00 4.04e-01 3.40 0.00068 ***
credit_historydelayed previously 7.91e-01 4.49e-01 1.76 0.07799 .
credit_historyexisting paid 7.12e-01 3.79e-01 1.88 0.06030 .
credit_historyno credits/all paid -1.19e-01 5.27e-01 -0.23 0.82161
purposedomestic appliance -2.58e-01 7.76e-01 -0.33 0.74004
purposeeducation -9.26e-01 4.57e-01 -2.03 0.04263 *
purposefurniture/equipment -4.22e-02 3.42e-01 -0.12 0.90175
purposenew car -7.83e-01 3.27e-01 -2.39 0.01675 *
purposeother 6.52e-01 7.83e-01 0.83 0.40495
purposeradio/tv 1.37e-01 3.29e-01 0.42 0.67733
purposerepairs -6.40e-01 5.81e-01 -1.10 0.27036
purposeretraining 1.38e+00 1.24e+00 1.11 0.26523
purposeused car 8.25e-01 4.29e-01 1.92 0.05449 .
credit_amount -1.29e-04 4.22e-05 -3.07 0.00217 **
savings_status>=1000 1.29e+00 5.07e-01 2.54 0.01101 *
savings_status100<=X<500 3.28e-01 2.77e-01 1.19 0.23548
savings_status500<=X<1000 4.30e-01 3.93e-01 1.09 0.27390
savings_statusno known savings 9.63e-01 2.57e-01 3.75 0.00018 ***
installment_commitment -3.30e-01 8.55e-02 -3.86 0.00011 ***
personal_statusmale div/sep -2.87e-01 3.76e-01 -0.76 0.44533
personal_statusmale mar/wid 1.30e-01 3.06e-01 0.42 0.67180
personal_statusmale single 5.36e-01 1.97e-01 2.71 0.00669 **
other_partiesguarantor 1.53e+00 5.58e-01 2.74 0.00619 **
other_partiesnone 4.87e-01 4.00e-01 1.22 0.22265
age 1.31e-02 8.40e-03 1.56 0.11897
other_payment_plansnone 6.99e-01 2.35e-01 2.98 0.00292 **
other_payment_plansstores 7.86e-02 4.03e-01 0.19 0.84539
housingown 2.92e-01 2.89e-01 1.01 0.31213
housingrent -1.50e-01 3.41e-01 -0.44 0.66079
own_telephoneyes 2.79e-01 1.84e-01 1.52 0.12939
foreign_workeryes -1.38e+00 6.21e-01 -2.23 0.02593 *
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
(Dispersion parameter for binomial family taken to be 1)
Null deviance: 1221.7 on 999 degrees of freedom
Residual deviance: 910.5 on 964 degrees of freedom
AIC: 982.5
Number of Fisher Scoring iterations: 5
Consider classifying the observations as 1 or 0 according to \(\hat \pi_i\):
Choose a threshold \(c \in [0,1]\) and make the classification \[ \hat Y_i = \left\{ \begin{array}{ll} 1,& \hat \pi_i \geq c\\ 0,& \hat \pi_i < c. \end{array}\right. \]
Can compute misclassification rate \(\#\{\hat Y_i \neq Y_i\}/n\).
Can compute observed true positive and false positive rates \[ \text{TP} = \frac{\#\{\hat Y_i = 1 \cap Y_i = 1\}}{\#\{Y_i = 1\}} \quad \text{ and } \quad \text{FP} = \frac{\#\{\hat Y_i = 1 \cap Y_i = 0\}}{\#\{Y_i = 0\}}. \]
Plotting TP against FP over all \(c \in [0,1]\) creates the receiver operating characteristic (ROC) curve.
Compute TP and FP over a range of thresholds \(c\). Plot ROC curve.
Can use ROC curves to compare models.
Overfitting is fitting a model such that it performs well on the data used to fit it, but will perform poorly when it is given new data.
To avoid, split data into training and testing data sets \[ \{(\mathbf{x}_i^{\text{train}},Y_i^{\text{train}})\}_{i \in \mathcal{I}_{\text{train}}} \quad \text{ and } \quad \{(\mathbf{x}_i^{\text{test}},Y_i^{\text{test}})\}_{i \in \mathcal{I}_{\text{test}}}, \] where \(\mathcal{I}_{\text{train}} \cap \mathcal{I}_{\text{test}} = \emptyset\) and \(\mathcal{I}_{\text{train}} \cup \mathcal{I}_{\text{test}} = \{1,\dots,n\}\).
Fit models on training data. Compare performance on testing data.
For example, compute misclassification rate or draw ROC curve for testing data.
set.seed(1)
n <- nrow(credg)
n_train <- ceiling(.5*n)
n_test <- n - n_train
ind_train <- sample(n,n_train) # randomly select a training set
credg_train <- credg[ind_train,]
credg_test <- credg[-ind_train,]
glm_train <- glm(class ~ ., family = "binomial", data = credg_train)
ypred_train <- glm_train$fitted.values >= 0.5
y_train <- as.numeric(credg_train$class == "good")
misclass_train <- mean(ypred_train != y_train)
misclass_train[1] 0.204
[1] 0.258