1 3.1 Transformations and adjustments

1.0.1 Written response

In this section, the main idea is that you often want your data to be on a “fair” scale before you start modeling. For example, comparing GDP across countries is misleading if you don’t account for population size, so GDP per capita is a more meaningful measure. Transformations are also useful when the variability changes as the level changes (a common pattern in economic and production series). A log or Box–Cox transformation can stabilize the variance, which usually makes trends and seasonal effects easier to see and helps models behave better.

1.1 3.1.1 Population adjustment example (GDP per capita)

global_economy |>
  filter(Country == "Australia") |>
  autoplot(GDP / Population) +
  labs(
    title = "Australian GDP per capita",
    y = "USD (GDP / Population)",
    x = "Year"
  )

1.1.1 Brief interpretation

Australia’s GDP per capita rises over time, with some noticeable slowdowns and dips around major economic events. The overall upward movement is clearer here than it would be using total GDP, since population growth is no longer mixed into the trend.

1.2 3.1.2 Mathematical transformations (Box–Cox / log)

We’ll use quarterly gas production and estimate a Box–Cox lambda using the Guerrero method.

gas <- aus_production |>
  filter_index("1956 Q1" ~ "2010 Q4") |>
  select(Gas)

gas |>
  autoplot(Gas) +
  labs(title = "Australian Gas Production (Quarterly)", y = "Gas", x = "Quarter")

lambda_tbl <- gas |>
  features(Gas, guerrero)

lambda_tbl
## # A tibble: 1 × 1
##   lambda_guerrero
##             <dbl>
## 1           0.110
lambda <- lambda_tbl$lambda_guerrero

gas |>
  mutate(Gas_boxcox = box_cox(Gas, lambda)) |>
  autoplot(Gas_boxcox) +
  labs(
    title = paste0("Gas after Box–Cox Transformation (lambda = ", round(lambda, 3), ")"),
    y = "Box–Cox(Gas)",
    x = "Quarter"
  )

1.2.1 Brief interpretation

The transformed series usually looks “more even” in terms of variability, especially if the original series has bigger swings in later years. That’s the practical reason for doing this: it can prevent later observations from dominating the fit just because the scale got larger.

2 3.2 Time series components

2.0.1 Written response

Time series are often easier to understand when you separate them into pieces: a long-run trend (and cycle), repeating seasonal patterns, and a leftover remainder that captures irregular movement. When seasonality is strong, it can hide the underlying direction of the series, so decomposition is a helpful diagnostic step before forecasting. STL is especially nice because it is flexible and tends to work well even when seasonality changes slowly over time.

2.1 3.2.1 STL decomposition example (US retail employment)

us_retail_employment <- us_employment |>
  filter(Title == "Retail Trade") |>
  select(Month, Employed)

us_retail_employment |>
  autoplot(Employed) +
  labs(
    title = "US Employment: Retail Trade",
    y = "Employed (thousands)",
    x = "Month"
  )

fit_stl <- us_retail_employment |>
  model(STL(Employed))

components(fit_stl) |>
  autoplot() +
  labs(title = "STL Decomposition: US Retail Employment")

2.1.1 Brief interpretation

Retail employment shows a clear seasonal pattern (higher and lower months repeating each year) plus an underlying trend that changes over time. The remainder still has noticeable spikes, which likely correspond to unusual periods where employment shifted more abruptly than the usual seasonal swing.

3 3.3 Moving averages

3.0.1 Written response

A moving average is a simple smoothing method: it replaces each value with an average of nearby values. This is useful when you want a quick estimate of the trend-cycle without fitting a more complicated model. With monthly data, a 12-month window is a natural choice to smooth out seasonality. In practice, centering matters; if the average isn’t centered, the smoothed line can lag the real trend.

3.1 3.3.1 13-term centered moving average (monthly example)

retail_short <- us_retail_employment |>
  filter_index("2010 Jan" ~ "2020 Dec")

retail_short |>
  autoplot(Employed) +
  labs(title = "US Retail Employment (2010–2020)", y = "Employed (thousands)")

retail_ma <- retail_short |>
  mutate(
    MA13 = slide_dbl(Employed, mean, .before = 6, .after = 6, .complete = TRUE)
  )

retail_ma |>
  autoplot(Employed) +
  geom_line(aes(y = MA13), linewidth = 1) +
  labs(
    title = "US Retail Employment with 13-term Centered Moving Average",
    y = "Employed (thousands)",
    x = "Month"
  )

3.1.1 Brief interpretation

The moving average smooths away the month-to-month bounce and makes the underlying direction easier to read. You can see longer stretches of growth or decline more clearly than in the raw series.

4 3.4 Classical decomposition

4.0.1 Written response

Classical decomposition is one of the older approaches to splitting a series into trend, seasonal, and remainder components. It’s based on moving averages and assumes the seasonal pattern is stable over time. That assumption can be a limitation, but it’s still a useful baseline because it’s straightforward and the output is easy to interpret. Using a multiplicative form makes sense when the seasonal swing grows with the level of the series.

4.1 3.4.1 Classical decomposition (multiplicative)

classical_fit <- us_retail_employment |>
  model(
    classical_decomposition(Employed, type = "multiplicative")
  )

components(classical_fit) |>
  autoplot() +
  labs(title = "Classical Decomposition (Multiplicative): US Retail Employment")

4.2 3.4.2 Seasonally adjusted series

Note: We only keep Month and season_adjust before joining to avoid duplicate column names.

sa <- components(classical_fit) |>
  as_tibble() |>
  select(Month, season_adjust)

sa_ts <- us_retail_employment |>
  left_join(sa, by = "Month")

sa_ts |>
  autoplot(Employed) +
  geom_line(aes(y = season_adjust), linewidth = 1) +
  labs(
    title = "Original vs Seasonally Adjusted (Classical Decomposition)",
    y = "Employed (thousands)",
    x = "Month"
  )

4.2.1 Brief interpretation

The seasonally adjusted line removes the regular within-year pattern, so what’s left is a cleaner view of the underlying movement in retail employment. This is helpful when you want to compare month-to-month changes without seasonal “noise.”

5 Reproducibility

sessionInfo()
## R version 4.5.2 (2025-10-31)
## Platform: x86_64-pc-linux-gnu
## Running under: Ubuntu 24.04.3 LTS
## 
## Matrix products: default
## BLAS:   /usr/lib/x86_64-linux-gnu/openblas-pthread/libblas.so.3 
## LAPACK: /usr/lib/x86_64-linux-gnu/openblas-pthread/libopenblasp-r0.3.26.so;  LAPACK version 3.12.0
## 
## locale:
##  [1] LC_CTYPE=C.UTF-8       LC_NUMERIC=C           LC_TIME=C.UTF-8       
##  [4] LC_COLLATE=C.UTF-8     LC_MONETARY=C.UTF-8    LC_MESSAGES=C.UTF-8   
##  [7] LC_PAPER=C.UTF-8       LC_NAME=C              LC_ADDRESS=C          
## [10] LC_TELEPHONE=C         LC_MEASUREMENT=C.UTF-8 LC_IDENTIFICATION=C   
## 
## time zone: UTC
## tzcode source: system (glibc)
## 
## attached base packages:
## [1] stats     graphics  grDevices utils     datasets  methods   base     
## 
## other attached packages:
##  [1] slider_0.3.3      fable_0.4.1       feasts_0.4.2      fabletools_0.5.1 
##  [5] tsibbledata_0.4.1 tsibble_1.1.6     ggplot2_4.0.1     lubridate_1.9.4  
##  [9] tidyr_1.3.2       dplyr_1.1.4       tibble_3.3.0      fpp3_1.0.2       
## 
## loaded via a namespace (and not attached):
##  [1] rappdirs_0.3.3       sass_0.4.10          generics_0.1.4      
##  [4] anytime_0.3.12       digest_0.6.39        magrittr_2.0.4      
##  [7] evaluate_1.0.5       grid_4.5.2           timechange_0.3.0    
## [10] RColorBrewer_1.1-3   fastmap_1.2.0        jsonlite_2.0.0      
## [13] purrr_1.2.0          scales_1.4.0         jquerylib_0.1.4     
## [16] cli_3.6.5            rlang_1.1.6          crayon_1.5.3        
## [19] ellipsis_0.3.2       withr_3.0.2          cachem_1.1.0        
## [22] yaml_2.3.12          tools_4.5.2          vctrs_0.6.5         
## [25] R6_2.6.1             lifecycle_1.0.4      pkgconfig_2.0.3     
## [28] warp_0.2.2           progressr_0.18.0     pillar_1.11.1       
## [31] bslib_0.9.0          gtable_0.3.6         glue_1.8.0          
## [34] Rcpp_1.1.0           xfun_0.55            tidyselect_1.2.1    
## [37] rstudioapi_0.17.1    knitr_1.51           farver_2.1.2        
## [40] htmltools_0.5.9      rmarkdown_2.30       labeling_0.4.3      
## [43] compiler_4.5.2       S7_0.2.1             distributional_0.5.0