Skip to main content

Section 8.8 Checking the residuals

To make sure that our data for the line looks good, it is important to answer the question:.

How close are our predicted values to our actual values?
To answer this question, we need to figure out the predicted values and then their residuals (the difference between the actual vs the predicted value).
We can do this in R with the two commands below.
pizza_sales <- pizza_sales |>
  mutate(predicted = predict(pizza_lm),
         residuals = residuals(pizza_lm))
To visually see the difference between the actual values and the predicted values, we can create a similar scatterplot as before, but add lines connecting our line of best fit and our points.
# What if we want to see actual vs predicted on our plot?
pizza_plot_residuals <- ggplot(pizza_sales, aes(x = price, y = volume)) +
  geom_point(size = 3)  +
  geom_smooth(method = "lm", se = FALSE) +
  geom_segment(aes(xend = price, yend = predicted), color = "gray", linewidth = 0.7) +
  theme_minimal() +
  labs(
    title = "Analysis of Pizza Sales",
    subtitle = "Gray lines show residuals (differences between actual and model predictions)",
    x = "Price ($)", y = "Volume"
  )

pizza_plot_residuals
A scatterplot with price on the x-axis and volume on the y-axis. Gray vertical lines connect each observed point to the fitted regression line, visually representing the residuals for each observation.
Figure 8.8.1. Scatterplot of pizza price and sales volume with residual lines connecting observed values to model predictions. Each gray line represents a residual, illustrating the difference between actual sales and values predicted by the linear regression model.
With our residuals, we want them to be random. Visually, that means that the residual values should be scattered on an x-y plane without a pattern. To see this, we can graph the residuals themselves.
# Let us graph our residuals to make sure they are random
ggplot(pizza_sales, aes(x = predicted, y = residuals)) +
  geom_point(color = "blue") +
  geom_hline(yintercept = 0, linetype = "dashed") +
  geom_smooth(se = FALSE, color = "red", linetype = "dotted")+
  labs(
    title = "Residuals Plot",
    x = "Predicted Volume",
    y = "Residuals"
  )
A scatter plot with predicted volume on the x-axis and residuals on the y-axis. Blue points are scattered around a dashed horizontal line at zero, with a dotted red smooth curve overlaid to show any systematic pattern in the residuals.
Figure 8.8.2. Residuals plotted against predicted sales volume from the linear regression model. Random scatter around the horizontal zero line indicates homoscedasticity, while systematic patterns would suggest violations of regression assumptions.
The red line in the graph above has a slight curve, showing that our model is less accurate at the extremes (low and high prices). Thankfully, this pattern isn’t very pronounced.

To accompany the visual, we want to statistically test for heteroscedasticity, meaning:.

Are the residuals roughly equal in variance across all predicted values?
We will either see:
  • Homoscedasticity: residuals are equally spread (good)
  • Heteroscedasticity: residuals get wider or narrower as predictions change (bad)
The Breusch-Pagan test provides insights for this. To perform this in R, we will use the bptest() function from the lmtest package.
library(lmtest)

bptest(pizza_lm)
studentized Breusch-Pagan test

data:  pizza_lm
BP = 7.7392, df = 1, p-value = 0.005403
The main focus point here is the p-value:
  • > 0.05 Fail to reject Hβ‚€ β†’ residuals are homoscedastic (good)
  • < 0.05 Reject Hβ‚€ β†’ residuals are heteroscedastic (not ideal)
Since our p-value is less than .05, our residuals are heteroscedastic.
This does not invalidate our model, and for right now, we do not need to change it. It is just showing us that it is not perfect, and not all prices predict equally well. In reality, this might happen if higher-priced pizzas are sold less frequently, giving us fewer data points and more variability.
Congratulations, we have just completed our first linear regression model!