1 3.1 Transformations and adjustments

FPP3 discusses how adjusting data (calendar, population, inflation) and applying transformations (log/Box-Cox) can simplify patterns and improve modeling.

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.2 3.1.2 Mathematical transformations (Box-Cox / log)

We’ll use a quarterly production series (Gas) and show how to choose 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")

# Estimate lambda for Box-Cox using Guerrero method
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"
  )

2 3.2 Time series components

A time series can be thought of as having: - Trend-cycle - Seasonality - Remainder

We’ll use the US retail employment example and decompose it with STL to visualize components.

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")

3 3.3 Moving averages

Moving averages smooth a series to estimate the trend-cycle.

We’ll compute a centered moving average on a monthly series using slider.

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)")

3.1 3.3.1 13-term centered moving average (smooths seasonality well)

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"
  )

4 3.4 Classical decomposition

Classical decomposition estimates: - Trend-cycle via moving averages - Seasonal component from detrended series - Remainder as leftover noise

We’ll use classical_decomposition() from feasts (included via fpp3).

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

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

4.1 3.4.1 Seasonally adjusted series

Fix for your error: when joining components back, we only keep Month and season_adjust to avoid creating Employed.x / Employed.y.

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"
  )

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