Logistic Regression in R: Complete Walkthrough with glm()

Concept card explaining logistic regression in R begins where linear regression fails

Logistic regression in R models a binary outcome, like pass or fail. It answers yes-or-no questions that linear regression handles badly. The model fits with glm(), a function built into base R. No package install happens, and no extra setup blocks your start. Students reach logistic regression the moment a linear model breaks. The outcome has two categories, so the straight-line fit fails. This walkthrough fits the model, reads the output, and checks it. Each step uses a built-in dataset and runs in RStudio.

Use Logistic Regression When the Outcome Has Two Categories

Logistic regression fits any outcome with exactly two categories. Pass or fail, yes or no, default or repay all qualify. Linear regression assumes a continuous outcome that runs without limits. A probability stays between 0 and 1, so the straight line fails. The linear fit predicts values below 0 and above 1. Those predictions describe impossible probabilities, like a 130 percent chance of passing.

This failure shows up clearly in the regression diagnostics. The residuals form bands instead of a random cloud. The Residuals vs Fitted plot bends, and the Q-Q plot curves. Regression Assumptions in R: The 4 Diagnostic Plots Explained shows each failure. When the outcome is binary, no transformation rescues the linear model. Linear Regression in R: Complete Walkthrough with Diagnostics shows the model logistic replaces.

Logistic regression solves this by modeling the log-odds instead. The output stays bounded between 0 and 1 at every point. The next section fits the model with one line of code.

The glm() Function Handles Logistic Regression Without a Package

Base R fits logistic regression through the glm() function. The glm() function ships with every R installation automatically. No package install, no library() call, and no setup delay. The function name stands for generalized linear model. One argument turns it into logistic regression: family = binomial.

The full call looks like glm(outcome ~ predictor, family = binomial). The family argument tells R which model type to fit. Leave it out, and glm() fits a plain linear model. Set family = binomial, and glm() fits the logistic version. This single argument separates the linear and logistic models.

Some students search for a logistic regression package and find dozens. Those packages add extras, like ROC curves or odds ratio tables. The core model still fits with base R and glm(). The next section runs the fit on a built-in dataset.

Fit the Model With One Line of glm() Code

One line of glm() code fits a logistic regression in R. This walkthrough uses mtcars, a dataset built into R. The data loads automatically, so no import step runs first. The outcome is am, the transmission type of each car. It holds two values, 0 for automatic and 1 for manual. The predictor is wt, the car weight in 1,000-pound units. The model asks whether weight predicts the transmission type.

model <- glm(am ~ wt, data = mtcars, family = binomial)

The formula am ~ wt reads as am explained by wt. The data argument names the dataset with both columns. The family = binomial argument sets the logistic model type. R stores the result in an object named model. A successful fit prints nothing and produces no error. The model object holds the coefficients, deviance, and fitted values.

The next section reads the model output one line at a time.

Read the Output as Log-Odds, Then Convert to Odds Ratios

The summary() function prints the full logistic regression output. Run it on the model object stored earlier.

summary(model)
Call:
glm(formula = am ~ wt, family = binomial, data = mtcars)

Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 12.040 4.510 2.670 0.00759 **
wt -4.024 1.436 -2.801 0.00509 **
---
Signif. codes: 0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1

Null deviance: 43.230 on 31 degrees of freedom
Residual deviance: 19.176 on 30 degrees of freedom
AIC: 23.176

Number of Fisher Scoring iterations: 6

The Coefficients table holds the two numbers that matter most. The Estimate column shows 12.040 for the intercept and -4.024 for wt. These estimates are log-odds, not probabilities or plain odds. A negative wt estimate means heavier cars lower the manual odds. The sign points the direction, and the size sets the strength.

Log-odds resist plain reading, so convert them to odds ratios. The exp() function turns a log-odds estimate into an odds ratio.

exp(coef(model))
(Intercept) wt
169459.583 0.018

The wt odds ratio is 0.018, a value below 1. Each extra 1,000 pounds multiplies the manual odds by 0.018. Heavier weight cuts the odds of a manual transmission sharply. An odds ratio above 1 raises the odds instead. An odds ratio of exactly 1 means the predictor does nothing.

The Pr(>|z|) column reports the p-value for each estimate. The wt p-value is 0.005, well below the 0.05 mark. The predictor reaches significance, so the weight effect is reliable. Two stars beside the row flag this significance level.

Two deviance lines and an AIC value report overall model fit. The checking section reads those numbers in detail.

Turn Predictions Into Classes With a Threshold

A threshold turns each predicted probability into a class label. The model outputs a probability for each car, not a class. The predict() function returns those probabilities with one argument.

predict(model, type = "response")

The type = “response” argument returns probabilities on the 0 to 1 scale. Without it, predict() returns log-odds instead, which resist reading. A heavy car at 3.5 thousand pounds scores 0.115. A light car at 2.5 thousand pounds scores 0.879. The light car reads as a likely manual, the heavy car as automatic.

A probability is not yet a class, so a threshold splits it. The standard threshold is 0.5, the midpoint of the scale. Probabilities above 0.5 become 1, and the rest become 0.

mtcars$prob <- predict(model, type = "response")
mtcars$pred <- ifelse(mtcars$prob > 0.5, 1, 0)
table(mtcars$am, mtcars$pred)
0 1
0 18 1
1 2 11

The ifelse() function assigns 1 above the threshold and 0 below. The table() function compares predicted classes against actual classes. Rows show the actual transmission, columns show the predicted one. The top-left 18 counts automatics predicted correctly as automatic. The bottom-right 11 counts manuals predicted correctly as manual. The top-right 1 is an automatic predicted wrongly as manual. The bottom-left 2 are manuals predicted wrongly as automatic. The diagonal holds 29 correct predictions out of 32 cars. The model reaches 91 percent accuracy on this data.

The 0.5 threshold suits balanced data with similar class costs. A different cost shifts the threshold up or down. Raising it to 0.7 demands more confidence before predicting a manual.

Check the Model With AUC and Deviance

Accuracy alone hides weak models, so two checks go deeper. The AUC score and the deviance values both rate the fit. The AUC measures how well the model ranks the two classes. It runs from 0.5, pure guessing, up to 1.0, perfect separation. The pROC package computes the AUC from the predicted probabilities.

library(pROC)
roc_obj <- roc(mtcars$am, mtcars$prob)
auc(roc_obj)
Area under the curve: 0.933

The AUC here is 0.933, close to the 1.0 ceiling. The model separates manuals from automatics with high reliability. An AUC near 0.5 signals a model no better than chance. The pROC package adds this metric, while base R fits the model.

The summary output reported two deviance lines earlier. Null deviance is 43.23, the fit with no predictor. Residual deviance is 19.18, the fit with weight added. The drop of 24.05 shows weight explains real variation. A large drop means the predictor improves the model.

AIC is 23.18, a score for comparing rival models. Lower AIC marks the better fit between two models. AIC matters only in comparison, never as a lone number.

This model holds one predictor, so multicollinearity does not arise. Models with several predictors check it with vif() from the car package. A VIF above 5 flags predictors that overlap too much.
One warning still trips many students during the fit. The next section fixes the errors logistic regression throws.

Fix the Two Errors Logistic Regression Throws Most

Two messages stop most logistic regressions in R, and both have quick fixes.

Code the Outcome as 0, 1, or a Two-Level Factor

R throws an error when the outcome is not binary. The binomial family expects values between 0 and 1 only. A character outcome or a count outcome triggers this message:

Error in eval(family$initialize) : y values must be 0 <= y <= 1

The fix converts the outcome to a factor with two levels.
mtcars$am <- factor(mtcars$am)
model <- glm(am ~ wt, data = mtcars, family = binomial)

R reads the first factor level as 0 and the second as 1. A yes/no column converts the same way with factor(). Check the outcome before fitting, since the error stops the model.

The Separation Warning Means a Predictor Splits the Data Perfectly

This warning appears when a predictor separates the classes perfectly. The model fit still runs, but the coefficients become unreliable. R prints two warning lines together below the fit:

Warning messages:
1: glm.fit: algorithm did not converge
2: glm.fit: fitted probabilities numerically 0 or 1 occurred

Perfect separation means one predictor predicts the outcome with no overlap. The standard errors explode, and the coefficients lose all meaning. The fix starts by finding the predictor that separates the data. Removing it or combining categories often resolves the warning. Penalized methods from the logistf package handle stubborn cases.

These two messages cover most logistic regression failures students hit. The final section connects the model to a graded assignment.

Take the Model From Console to Graded Submission

A graded logistic regression combines the fit, the read, and the write-up. This walkthrough fits the model with glm() and the binomial family. It reads the coefficients as log-odds, then as odds ratios. It turns probabilities into classes with a 0.5 threshold. It checks the model with AUC, deviance, and AIC. It fixes the factor-outcome error and the separation warning.

An assignment asks for more than working code. Markers want the odds ratios explained in plain words. They want the threshold justified and the AUC reported. The write-up carries as many marks as the code.

Some assignments arrive with an unfamiliar dataset and a tight deadline. Your expert works through the model directly in RStudio. Your expert uses the methods taught in your own class. We match you with that expert after you describe the task. You talk with your expert before any payment starts. R Programming, RStudio and Statistics Homework Help opens that conversation.
Logistic regression in R rests on one function and a clear read. The model fits in one line, and the output tells the story.

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top