What it does: The Box-Cox method is a family of mathematical power transformations used to stabilize the variance of a time series. In many real-world time series (like the Australian monthly electricity production example), you will notice that as the overall level of the series increases, the size of the seasonal fluctuations or variance also increases. Box-Cox addresses this by applying a transformation indexed by a parameter, \(\lambda\), to keep the variation steady across the entire series.
The transformation formula is: \[w_t = \begin{cases} \ln(y_t) & \text{if } \lambda = 0 \\ \frac{(y_t^\lambda - 1)}{\lambda} & \text{if } \lambda \neq 0 \end{cases}\]
Advantages:
Variance Stabilization: It ensures that the seasonal swings remain roughly constant over time. This is critical because many forecasting models (like linear regression or ARIMA) assume a constant variance in their errors.
Flexibility: Because statistical software automatically calculates the optimal \(\lambda\), it seamlessly chooses between a log transformation (\(\lambda = 0\)), no transformation (\(\lambda = 1\)), an inverse transformation (\(\lambda = -1\)), or a fractional power like a square root (\(\lambda = 0.5\)).
Improved Normality: It often makes the distribution of the errors more normal, which improves the reliability of your prediction intervals.
Disadvantages:
Loss of Interpretability: Once transformed, the data is no longer in its original scale or units. Explaining “transformed electricity production” to stakeholders can be difficult.
Back-Transformation Bias: Point forecasts generated from the transformed data must be mathematically back-transformed to the original scale. Simple back-transformation often yields the median of the forecast distribution rather than the mean, leading to a slight bias unless an adjustment factor is explicitly added.
Question 2: Basic Model Implementation
For this implementation, I am using the classic AirPassengers dataset built into R, which tracks monthly airline passengers from 1949 to 1960. As the overall number of passengers increases over the decade, the seasonal peaks and valleys become much wider, making it a great candidate to test variance-focused metrics.
I am implementing a basic Linear Regression with trend and seasonal time dummies. Then, I will calculate both bias-focused metrics (Mean Error - ME, Mean Percentage Error - MPE) and variance-focused metrics (Root Mean Squared Error - RMSE, Mean Absolute Error - MAE).
# Load necessary librarieslibrary(forecast)library(ggplot2)# Load the datasetdata("AirPassengers")# Fit a basic linear regression with trend and seasonal dummiesfit_basic <-tslm(AirPassengers ~ trend + season)# Calculate accuracy metricsacc_basic <-accuracy(fit_basic)# Print the specific metrics requestedprint("Accuracy Metrics - Basic Linear Regression:")
[1] "Accuracy Metrics - Basic Linear Regression:"
print(acc_basic[, c("ME", "MPE", "RMSE", "MAE")])
ME MPE RMSE MAE
1.999781e-16 6.039558e-01 2.511363e+01 1.977364e+01
# Plot the original data against our basic fitted modelautoplot(AirPassengers, series="Actual Data") +autolayer(fitted(fit_basic), series="Basic tslm() Fit") +ggtitle("AirPassengers: Actuals vs. Basic Model (No Transformation)") +ylab("Number of Passengers") +xlab("Year")
Question 3: Applying Box-Cox Transformation
Now, I will apply the Box-Cox transformation to the same dataset and model to see if we can reduce our error metrics and better capture the increasing variance.
# Find the optimal lambda for the datasetlambda_opt <-BoxCox.lambda(AirPassengers)cat("Optimal Lambda calculated:", lambda_opt, "\n\n")
Optimal Lambda calculated: -0.2947156
# Fit the same linear regression model, but applying the Box-Cox transformationfit_bc <-tslm(AirPassengers ~ trend + season, lambda = lambda_opt)# Calculate accuracy metricsacc_bc <-accuracy(fit_bc)# Print the specific metrics for comparisonprint("Accuracy Metrics - Box-Cox Transformed Model:")
ME MPE RMSE MAE
-1.669582 -0.265642 24.409771 18.369936
# Plot the original data against our new Box-Cox fitted modelautoplot(AirPassengers, series="Actual Data") +autolayer(fitted(fit_bc), series="Box-Cox Fitted Model") +ggtitle(paste("AirPassengers: Box-Cox Fit (lambda =", round(lambda_opt, 2), ")")) +ylab("Number of Passengers") +xlab("Year")
Findings and InterpretationDid the error metrics reduce?Yes. The BoxCox.lambda() function calculated the optimal \(\lambda\) to be approximately -0.29. By passing this \(\lambda\) into our tslm() model, the function automatically transforms the data, fits the model, and back-transforms the fitted values to the original scale so we can directly compare the accuracy metrics.Comparing the variance-focused metrics, both the RMSE and MAE dropped significantly compared to the basic un-transformed model in Question 2. This shows that the model fits the data much better because the linear regression no longer struggles to capture the increasingly wide seasonal peaks at the end of the 1950s.What happens to the interpretation?In our original model (Question 2), the coefficients represented strict additive changes: “Being in the month of July adds an absolute number of X passengers compared to the baseline month.” Once we apply the Box-Cox transformation (Question 3), the interpretation completely changes. Because our optimal \(\lambda\) (-0.29) is relatively close to a log transformation (0), the relationship shifts from additive to multiplicative. The seasonal dummy coefficients no longer represent an absolute number of passengers added; instead, they represent a proportional or percentage increase depending on the current level of the trend.