31 Splines and LOESS: Flexible Regression Curves

31.1 Splines and LOESS: Flexible Regression Curves

So far we have focused on parametric regression models, where the mean response is described by a small number of coefficients. In this lecture we use the Seoul Bike Sharing data to study a different problem: the relationship between demand and weather is clearly nonlinear.

By the end of this lecture, students should be able to:

  1. Explain why a straight-line mean function can be too restrictive.
  2. Use LOESS to explore nonlinear structure.
  3. Fit regression splines using basis functions.
  4. Explain the difference between ordinary and natural splines.
  5. Recognise how added flexibility creates the need for regularisation.

31.1.1 Running Example: Hourly Bike Demand

Download seoul_bike_hourly.csv

seoul_hourly = read_csv("../data/seoul_bike_hourly.csv")
seoul_hourly
# A tibble: 8,760 × 16
   date       datetime            day_index  hour rented_bike_count temperature
   <date>     <dttm>                  <dbl> <dbl>             <dbl>       <dbl>
 1 2017-12-01 2017-12-01 00:00:00         1     0               254        -5.2
 2 2017-12-01 2017-12-01 01:00:00         1     1               204        -5.5
 3 2017-12-01 2017-12-01 02:00:00         1     2               173        -6  
 4 2017-12-01 2017-12-01 03:00:00         1     3               107        -6.2
 5 2017-12-01 2017-12-01 04:00:00         1     4                78        -6  
 6 2017-12-01 2017-12-01 05:00:00         1     5               100        -6.4
 7 2017-12-01 2017-12-01 06:00:00         1     6               181        -6.6
 8 2017-12-01 2017-12-01 07:00:00         1     7               460        -7.4
 9 2017-12-01 2017-12-01 08:00:00         1     8               930        -7.6
10 2017-12-01 2017-12-01 09:00:00         1     9               490        -6.5
# ℹ 8,750 more rows
# ℹ 10 more variables: humidity <dbl>, wind_speed <dbl>, visibility <dbl>,
#   dew_point_temperature <dbl>, solar_radiation <dbl>, rainfall <dbl>,
#   snowfall <dbl>, season <chr>, holiday <chr>, functioning_day <chr>

The core question for this lecture is:

How does hourly bike demand change with temperature?

31.1.2 Why Go Beyond Straight Lines?

A straight-line model can underfit. A highly flexible curve can overfit. Our aim is to describe the mean relationship without losing the regression framing.

seoul_hourly |>
    ggplot(aes(x = temperature, y = rented_bike_count)) + geom_point(alpha = 0.06) +
    labs(x = "Temperature (C)", y = "Hourly rented bike count", title = "Bike demand changes nonlinearly with temperature")

unlabelled

31.2 Example 1: LOESS

31.2.1 The LOESS Idea

LOESS fits many small local regressions instead of one global line.

  1. Choose a target point.
  2. Borrow nearby observations.
  3. Fit a weighted local regression.

31.2.2 LOESS with the Seoul Bike Data

seoul_hourly |>
    ggplot(aes(x = temperature, y = rented_bike_count)) + geom_point(alpha = 0.05) +
    geom_smooth(method = "loess", formula = y ~ x, span = 0.15, se = FALSE, linewidth = 1,
        aes(colour = "Span = 0.15", alpha = "Span = 0.15")) + geom_smooth(method = "loess",
    formula = y ~ x, span = 0.45, se = FALSE, linewidth = 1, aes(colour = "Span = 0.45",
        alpha = "Span = 0.45")) + scale_colour_manual(values = c(`Span = 0.15` = "green4",
    `Span = 0.45` = "blue")) + scale_alpha_manual(values = c(`Span = 0.15` = 0.8,
    `Span = 0.45` = 0.8)) + labs(x = "Temperature (C)", y = "Hourly rented bike count",
    colour = "LOESS span", alpha = "LOESS span", title = "LOESS span controls the smoothness of the fitted curve")

unlabelled

31.2.3 The Span Parameter

  • span controls how local the fit is.
  • Small span gives a more flexible but noisier curve.
  • Large span gives a smoother and more stable curve.

LOESS is especially useful for seeing the empirical shape of the data before we decide on a formal regression model.

31.3 Example 2: Splines

31.3.1 The Key Conceptual Point

A spline model is still linear in its coefficients:

\[y = \beta_0 + \beta_1 b_1(x) + \beta_2 b_2(x) + \cdots + \beta_k b_k(x) + \varepsilon.\]

It is nonlinear in the original predictor, but linear in the unknown coefficients. That means we can still use standard regression tools.

31.3.2 Linear, Spline, and Natural Spline Fits

library(splines)

seoul_temp = seoul_hourly |>
    select(temperature, rented_bike_count) |>
    filter(is.finite(temperature), is.finite(rented_bike_count))

lm_linear = lm(rented_bike_count ~ temperature, data = seoul_temp)
lm_bs = lm(rented_bike_count ~ bs(temperature, df = 5), data = seoul_temp)
lm_ns = lm(rented_bike_count ~ ns(temperature, df = 5), data = seoul_temp)

temp_grid = tibble(temperature = seq(min(seoul_temp$temperature), max(seoul_temp$temperature),
    length.out = 250))

predictions = temp_grid |>
    mutate(Linear = predict(lm_linear, newdata = temp_grid), `B-spline` = predict(lm_bs,
        newdata = temp_grid), `Natural spline` = predict(lm_ns, newdata = temp_grid)) |>
    pivot_longer(cols = c(Linear, `B-spline`, `Natural spline`), names_to = "model",
        values_to = "fitted_bikes")

seoul_temp |>
    ggplot(aes(x = temperature, y = rented_bike_count)) + geom_point(alpha = 0.05) +
    geom_line(data = predictions, aes(x = temperature, y = fitted_bikes, colour = model,
        linetype = model, alpha = model), linewidth = 1) + scale_colour_manual(values = c(Linear = "red",
    `B-spline` = "blue", `Natural spline` = "forestgreen")) + scale_linetype_manual(values = c(Linear = "solid",
    `B-spline` = "dotted", `Natural spline` = "dotdash")) + scale_alpha_manual(values = c(Linear = 0.5,
    `B-spline` = 1, `Natural spline` = 1)) + labs(x = "Temperature (C)", y = "Hourly rented bike count",
    colour = "Model", linetype = "Model", alpha = "Model", title = "Splines allow flexible but still regression-based fits")

unlabelled

31.3.3 Natural Splines vs Ordinary Splines

predictions |>
    filter(model %in% c("B-spline", "Natural spline")) |>
    ggplot(aes(x = temperature, y = fitted_bikes, colour = model)) + geom_line(linewidth = 1) +
    labs(x = "Temperature (C)", y = "Fitted hourly bike count", colour = "Model",
        title = "Natural splines constrain the fit near the boundaries")

unlabelled

Natural splines are often a sensible default because we tend to trust the extremes of the predictor range less than the middle.

31.4 A Second Nonlinear Signal

Temperature is not the only candidate for nonlinear modelling. Solar radiation may also have a curved relationship with demand.

seoul_hourly |>
    ggplot(aes(x = solar_radiation, y = rented_bike_count)) + geom_point(alpha = 0.05) +
    geom_smooth(method = "loess", formula = y ~ x, se = FALSE, linewidth = 1) + labs(x = "Solar radiation",
    y = "Hourly rented bike count", title = "Other weather predictors may also need flexible mean functions")

unlabelled

31.5 Lecture Summary

  • LOESS is a flexible local smoother that is very useful for exploring shape.
  • Splines create flexible mean functions inside a regression framework.
  • Natural splines help control unstable boundary behaviour.

31.6 Bridge to the Next Lecture

The Seoul bike data now present a new problem. After modelling nonlinear weather effects, we switch from hourly demand to daily demand and ask how time itself can enter the regression model through trend and seasonal predictors.