geom_smooth draws a fit, but a paper needs the numbers behind the line: coefficients, uncertainties, R². This lesson fits the model explicitly with lm(), quotes it, and then plots exactly that model — so the line in the figure and the numbers in the text can never disagree.
Step 1 — Data with a trend
library(ggplot2)
set.seed(11)
df <- data.frame(dose = runif(40, 0, 10))
df$response <- 2.0 + 0.85 * df$dose + rnorm(40, 0, 0.9)
Step 2 — Fit and read the summary
fit <- lm(response ~ dose, data = df)
summary(fit)
In the printed summary: the dose row’s Estimate is your slope, Std. Error its uncertainty, and Multiple R-squared the fraction of variance explained. “response increased by 0.85 ± 0.05 per unit dose (R² = 0.87)” — every number comes from this one output.
Step 3 — Predict on a fine grid
Evaluate the fitted model on a smooth grid, asking for the confidence interval:
grid <- data.frame(dose = seq(0, 10, length.out = 200))
pred <- predict(fit, newdata = grid, interval = "confidence")
grid <- cbind(grid, as.data.frame(pred))
head(grid) # fit, lwr, upr per dose
Step 4 — Plot data, line, and ribbon
ggplot(df, aes(x = dose, y = response)) +
geom_ribbon(data = grid, aes(y = fit, ymin = lwr, ymax = upr),
alpha = 0.2) +
geom_line(data = grid, aes(y = fit), linewidth = 0.9) +
geom_point(size = 1.8) +
labs(x = "Dose (mg)", y = "Response") +
theme_minimal(base_size = 13)
Ribbon first, then line, then points — later layers draw on top, so the data stay visible over the model. This is the same picture geom_smooth(method = "lm") gives, except now it is your fit object drawing it — the one whose coefficients you quoted.
Step 5 — Beyond straight lines
lm fits anything linear in the parameters — a quadratic is one term away:
fit2 <- lm(response ~ dose + I(dose^2), data = df)
anova(fit, fit2)
anova compares the two: a large p-value says the quadratic term isn’t earning its place, which is worth a sentence in the paper all by itself.
Step 6 — Export or insert
Export / Insert — vector PDF, or into the paper with a \label, with the script saved as the figure’s source.
Tip:
interval = "confidence"bounds the line;interval = "prediction"bounds where new points would fall and is much wider. Quoting the narrow one when you mean the wide one is a classic reviewer catch — pick on purpose.