remove(list = ls())
# Set up
library(fpp3)
library(fredr)
library(tidyverse)
library(patchwork)
library(knitr)
library(kableExtra)
library(writexl)
library(urca)Module 4 Discussion
I. What is Box Cox?
Box Cox is a type of transformation used to stabilize variance in a time series, especially when variance increases with level. It follows this series of equations:
\[ y_t^{(\lambda)} =\begin{cases}\dfrac{y_t^\lambda - 1}{\lambda}, & \lambda \neq 0, \\\log(y_t), & \lambda = 0.\end{cases} \]
Where lambda defines the degree of curvature of the transformation. The main advantages of the Box Cox transformation are variance stabilization and improved residuals. Additionally, it often improves model selection criteria such as AIC and makes seasonal patterns easier to interpret and model. However, the transformation requires positive data and forecasts must be transformed back to the original scale. This can introduce bias. It also does not address non-stationarity in the mean, so differencing may still be required.
II. Simple Linear Regression
# Dataset
fredr_set_key("523a2b98a1ce120186357fd0c916cc26")
op <- fredr(series_id = "OILPRICE",
observation_start = as.Date("1980-01-01"),
observation_end = as.Date("2024-12-01")
) |>
transmute(Month = yearmonth(date), value) |>
as_tsibble(index = Month)After loading data, it has to be split into train and test, done below.
n <- nrow(op)
train_size <- floor(0.8 * n)
train <- op |> slice(1:train_size)
test <- op |> slice((train_size + 1):n)fit_basic <- train |>
model(
MEAN = MEAN(value),
DRIFT = RW(value ~ drift()),
SNAIVE = SNAIVE(value)
)h <- nrow(test)
fc_basic <- fit_basic |>
forecast(h = h)acc_basic <- fc_basic |>
accuracy(test)
acc_basic# A tibble: 3 × 10
.model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 DRIFT Test 21.9 28.9 24.3 21.6 27.0 NaN NaN 0.912
2 MEAN Test 57.0 60.2 57.0 65.6 65.6 NaN NaN 0.912
3 SNAIVE Test 18.4 26.6 22.3 17.1 25.1 NaN NaN 0.909
As the metrics show, snaive performs best overall, with the lowest RMSE (26.55), MAE (22.30), and MAPE (25.15). The mean model performs worst, with large bias (ME = 57.01) and error magnitudes, suggesting it systematically overpredicts.
After applying Box Cox, bias metrics such as ME and MPE should decrease and error spread measures like RMSE and MAE should improve slightly, particularly if variance increases with the level of oil prices.
III. Transformations
lambda_hat <- train |> features(value, guerrero) |> pull(lambda_guerrero)
train_t <- train |>
mutate(
y_none = value,
y_log = log(value),
y_sqrt = sqrt(value),
y_bc = box_cox(value, lambda_hat)
)
test_t <- test |>
mutate(
y_none = value,
y_log = log(value),
y_sqrt = sqrt(value),
y_bc = box_cox(value, lambda_hat)
)
fit_none <- train_t |>
model(
MEAN = MEAN(y_none),
DRIFT = RW(y_none ~ drift()),
SNAIVE = SNAIVE(y_none)
)
fit_log <- train_t |>
model(
MEAN = MEAN(y_log),
DRIFT = RW(y_log ~ drift()),
SNAIVE = SNAIVE(y_log)
)
fit_sqrt <- train_t |>
model(
MEAN = MEAN(y_sqrt),
DRIFT = RW(y_sqrt ~ drift()),
SNAIVE = SNAIVE(y_sqrt)
)
fit_bc <- train_t |>
model(
MEAN = MEAN(y_bc),
DRIFT = RW(y_bc ~ drift()),
SNAIVE = SNAIVE(y_bc)
)
fc_none <- fit_none |> forecast(h = h)
fc_log <- fit_log |> forecast(h = h)
fc_sqrt <- fit_sqrt |> forecast(h = h)
fc_bc <- fit_bc |> forecast(h = h)
acc_transformed <- bind_rows(
accuracy(fc_none, test_t) |> mutate(transformation = "None (level)"),
accuracy(fc_log, test_t) |> mutate(transformation = "Log"),
accuracy(fc_sqrt, test_t) |> mutate(transformation = "Sqrt"),
accuracy(fc_bc, test_t) |> mutate(transformation = paste0("Box-Cox (lambda=", round(lambda_hat, 3), ")"))
) |>
select(transformation, .model, ME, RMSE, MAE, MPE, MAPE, ACF1) |>
arrange(transformation, RMSE)
fc_log_bt <- fc_log |> mutate(.mean = exp(.mean))
fc_sqrt_bt <- fc_sqrt |> mutate(.mean = (.mean)^2)
fc_bc_bt <- fc_bc |> mutate(.mean = inv_box_cox(.mean, lambda_hat))
acc_level <- bind_rows(
accuracy(fc_none, test_t) |> mutate(transformation = "None (level)"),
accuracy(fc_log_bt, test_t) |> mutate(transformation = "Log (back-transformed)"),
accuracy(fc_sqrt_bt, test_t) |> mutate(transformation = "Sqrt (back-transformed)"),
accuracy(fc_bc_bt, test_t) |> mutate(transformation = paste0("Box-Cox (back-transformed, lambda=", round(lambda_hat, 3), ")"))
) |>
select(transformation, .model, ME, RMSE, MAE, MPE, MAPE, ACF1) |>
arrange(transformation, RMSE)
acc_level# A tibble: 12 × 8
transformation .model ME RMSE MAE MPE MAPE ACF1
<chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Box-Cox (back-transformed, lam… SNAIVE 2.54 3.73 3.18 11.4 16.1 0.907
2 Box-Cox (back-transformed, lam… DRIFT 3.00 3.99 3.42 13.9 17.2 0.913
3 Box-Cox (back-transformed, lam… MEAN 9.84 10.2 9.84 50.6 50.6 0.914
4 Log (back-transformed) SNAIVE 0.221 0.334 0.289 4.70 6.48 0.903
5 Log (back-transformed) DRIFT 0.252 0.346 0.301 5.43 6.71 0.911
6 Log (back-transformed) MEAN 1.18 1.21 1.18 26.6 26.6 0.914
7 None (level) SNAIVE 18.4 26.6 22.3 17.1 25.1 0.909
8 None (level) DRIFT 21.9 28.9 24.3 21.6 27.0 0.912
9 None (level) MEAN 57.0 60.2 57.0 65.6 65.6 0.912
10 Sqrt (back-transformed) SNAIVE 1.01 1.48 1.26 9.71 13.4 0.907
11 Sqrt (back-transformed) DRIFT 1.19 1.58 1.35 11.7 14.3 0.913
12 Sqrt (back-transformed) MEAN 4.00 4.15 4.00 43.1 43.1 0.914
On the original level, seasonal naive had an RMSE of 26.55 and MAPE of 25.15, whereas after log back-transformation, it’s RMSE drops dramatically to 0.33 and MAPE to 6.48. The Box Cox transformation (λ = 0.553) and square root transformation also reduce error from the level model, but not as much as the log. Across all specifications, seasonal naive consistently performs best among the benchmark models, while mean performs worst. This indicates that stabilizing variance materially improves forecast performance for oil prices, which likely exhibit level dependent volatility.
Once we apply a transformation such as the log or Box Cox, the model is no longer forecasting oil prices directly. It now forecasts the transformed series. After back-transformation, forecasts return to dollar units, but the underlying structure is multiplicative instead of additive. Transformations change both the scale of estimation and the economic interpretation of model components.