Linear Models (lm)
Reviewed & published by Brayan K
A linear model fits the best straight-line relationship between a response and one or more predictors — R's lm() function makes regression a single, readable line of code.
Part of the free R course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll fit a model with lm(), read its coefficients and R-squared, make predictions on new data, and build models with multiple predictors.
What You'll Learn in This Lesson
- How lm() fits a regression with response ~ predictor
- How to read the intercept and slope
- How summary() reports p-values and R-squared
- What R-squared measures
- How to predict() on new data
- How to model with multiple predictors
1️⃣ Fitting a Model
lm(response ~ predictor, data = df) fits the model. coef() pulls out the intercept and slope, which define the best-fit line.
# lm() fits a linear model. The formula is: response ~ predictor.
df <- data.frame(
hours = c(1, 2, 3, 4, 5),
score = c(52, 61, 68, 80, 89)
)
model <- lm(score ~ hours, data = df)
coef(model) # intercept and slope
# (Intercept) hours
# 42.1 9.3
# Read it as: score ≈ 42.1 + 9.3 * hours
# (each extra hour adds about 9.3 points)2️⃣ Reading the Model with summary()
summary(model) reports the full results — coefficient estimates, their p-values, and R-squared, the share of variation the model explains.
df <- data.frame(
hours = c(1, 2, 3, 4, 5),
score = c(52, 61, 68, 80, 89)
)
model <- lm(score ~ hours, data = df)
# summary() gives the full picture: coefficients, p-values, and R-squared.
summary(model)$r.squared # how much variation is explained (0-1)
# -> [1] 0.9941...
# Extract pieces:
summary(model)$coefficients[, "Estimate"] # the fitted coefficients3️⃣ Predicting and Multiple Predictors
predict(model, newdata) applies the fitted rule to new cases. Add predictors with + in the formula — mpg ~ wt + hp uses both weight and horsepower.
df <- data.frame(
hours = c(1, 2, 3, 4, 5),
score = c(52, 61, 68, 80, 89)
)
model <- lm(score ~ hours, data = df)
# predict() applies the model to NEW data.
new_data <- data.frame(hours = c(6, 7))
predict(model, new_data)
# 1 2
# 97.9 107.2
# Multiple predictors use + in the formula:
mtcars_model <- lm(mpg ~ wt + hp, data = mtcars)
round(coef(mtcars_model), 3)
# (Intercept) wt hp
# 37.227 -3.878 -0.032Your turn. Fill in the ___ blank, run it, and compare with the expected output.
# 🎯 YOUR TURN — replace each ___ then run it.
ads <- data.frame(
spend = c(10, 20, 30, 40, 50),
sales = c(100, 140, 190, 230, 280)
)
# 1) Fit a model predicting sales from spend
model <- lm(___, data = ads) # 👉 replace ___ with sales ~ spend
# 2) Print the slope (the "spend" coefficient), rounded to 2 decimals
cat("Slope:", round(coef(model)["spend"], 2), "\n")
# 3) Predict sales for a spend of 60
cat("Predicted at 60:", round(predict(model, data.frame(spend = 60)), 1), "\n")
# ✅ Expected output:
# Slope: 4.5
# Predicted at 60: 323Mini-Challenge: Model and Report
Write it from the outline, run it, and check it against the example output. Fitting, reporting R-squared, and predicting is the full regression workflow in miniature.
# 🎯 MINI-CHALLENGE: model and report.
#
# Use this data:
# temp <- c(15, 18, 20, 25, 30, 35)
# sales <- c(20, 24, 27, 36, 45, 52) (ice cream sales)
#
# 1. Fit lm(sales ~ temp).
# 2. Print the intercept and slope, each rounded to 2 decimals.
# 3. Print the R-squared from summary(model)$r.squared, rounded to 3.
# 4. Predict sales when temp = 28 with predict().
#
# ✅ Example output (approximate):
# Intercept: -7.5 Slope: 1.71
# R-squared: 0.996
# At 28 degrees: 40.4
# your code hereCommon Errors (and the fix)
- Formula reversed — lm(y ~ x) predicts y FROM x. Swapping them models the wrong relationship.
- predict() column mismatch — newdata must use the same predictor names as the formula. data.frame(hours = 6), not x = 6.
- Extrapolating too far — predicting well outside the training range is unreliable; the line may not hold there.
- Forgetting data = — pass the data frame so R finds the columns: lm(y ~ x, data = df).
Pro Tips
- 💡 Plot the fit: in ggplot2, geom_smooth(method = "lm") draws the same regression line visually.
- 💡 Check residuals: plot(model) produces diagnostic plots to test the model's assumptions.
- 💡 Categorical predictors work: include a factor in the formula and R creates the dummy variables for you.
📋 Quick Reference — Linear Models
| Task | R Syntax |
|---|---|
| Fit a model | lm(y ~ x, data = df) |
| Coefficients | coef(model) |
| Full report | summary(model) |
| R-squared | summary(model)$r.squared |
| Predict | predict(model, newdata) |
| Multiple predictors | lm(y ~ a + b) |
🎉 Lesson Complete!
- ✅ lm(y ~ x, data = df) fits a regression
- ✅ The intercept and slope define the best-fit line
- ✅ summary() gives p-values and R-squared
- ✅ R-squared is the share of variation explained
- ✅ predict() applies the model to new data
Practice quiz
How do you read the formula in lm(score ~ hours)?
- hours predicted from score
- score predicted from hours
- score equals hours
- hours times score
Answer: score predicted from hours. The form is response ~ predictor, so score is modelled from hours.
Which function pulls out a model's intercept and slope?
- coef(model)
- slope(model)
- params(model)
- lm(model)
Answer: coef(model). coef() returns the fitted coefficients (intercept and slope per predictor).
What does R-squared measure?
- The number of predictors
- The slope of the line
- The p-value
- The fraction of variation the model explains
Answer: The fraction of variation the model explains. R-squared, from 0 to 1, is the share of the response's variation explained.
How do you get R-squared from a fitted model?
- coef(model)$r2
- summary(model)$r.squared
- rsq(model)
- model$r.squared
Answer: summary(model)$r.squared. summary(model)$r.squared returns the R-squared value.
Which function applies a fitted model to new data?
- forecast()
- apply()
- predict()
- fit()
Answer: predict(). predict(model, newdata) computes predictions for new cases.
How do you add a second predictor in a formula?
- mpg ~ wt + hp
- mpg ~ wt, hp
- mpg ~ wt & hp
- mpg ~ wt * hp only
Answer: mpg ~ wt + hp. Add predictors with +, e.g. mpg ~ wt + hp uses both.
What must newdata in predict() contain?
- The response column
- Random values
- Nothing
- The same predictor column names as the formula
Answer: The same predictor column names as the formula. newdata must use the same predictor names the model was trained on.
If the slope for hours is 9.1, what does it mean?
- Each extra hour adds about 9.1 to the response
- The intercept is 9.1
- R-squared is 9.1
- There are 9.1 predictors
Answer: Each extra hour adds about 9.1 to the response. A slope is the change in response per one-unit increase in that predictor.
What does the intercept represent?
- The maximum response
- The predicted response when all predictors are zero
- The error term
- The number of rows
Answer: The predicted response when all predictors are zero. The intercept is the model's predicted value when predictors equal zero.
Why is predicting far outside the training range risky?
- It is always exact
- predict() refuses to run
- Extrapolation may not hold beyond the data
- It changes the coefficients
Answer: Extrapolation may not hold beyond the data. The linear relationship that held within your data may not continue beyond it.
Continue this course
- Previous: Checkpoint: Data Analysis
- Next: Writing Functions & Packages — Package reusable code, document it, and share it with others
- Quick reference: R cheat sheet › Stats & Models
Frequently asked questions
What does lm() do and how do I read its formula?
lm() fits a linear regression — it finds the straight-line relationship that best predicts a response variable from one or more predictors. The formula syntax is response ~ predictor, read as 'response explained by predictor'. So lm(score ~ hours) models score as a function of hours. The result is an object holding the fitted coefficients (an intercept and a slope per predictor) plus diagnostics you can inspect with summary().
How do I interpret the intercept and slope?
The intercept is the model's predicted value when all predictors are zero — the line's starting height. Each slope (coefficient) tells you how much the response changes for a one-unit increase in that predictor, holding others fixed. If the 'hours' slope is 9.1, then each extra hour of study is associated with about 9.1 more points. Signs matter: a negative slope means the response falls as the predictor rises.
What is R-squared and what counts as 'good'?
R-squared, in summary(model)$r.squared, is the fraction of the response's variation that the model explains, from 0 to 1. An R-squared of 0.85 means the model accounts for 85% of the variability. Higher is generally better, but 'good' depends entirely on the field — physics experiments may expect 0.99, while human-behaviour data might be impressive at 0.3. Don't chase R-squared blindly; a model can fit the sample well yet generalise poorly.
How do I make predictions with a fitted model?
Use predict(model, newdata), where newdata is a data frame with the same predictor column names the model was trained on. For lm(score ~ hours), you'd pass data.frame(hours = c(6, 7)) to predict scores for 6 and 7 hours. Be cautious about predicting far outside the range of your training data (extrapolation) — the linear relationship that held within your data may not continue beyond it.