data_624_hw05

Author

Maxfield Raynolds

8.1

a.

A plot of the pigs in the state of Victoria, Australia.

pigs <- aus_livestock |> filter(Animal == 'Pigs', State == 'Victoria')

autoplot(pigs, Count)

The code below fits a simple exponential smoothing model to the pig data. Forecasts 4 years, and extracts the alpha value and initial level for display.

pig_fit <- pigs |> 
  model(ses = ETS(Count ~ error('A') + trend('N') + season('N')))

pig_forecast <- pig_fit |> 
  forecast(h = 4)

alpha <- pig_fit |> 
  tidy() |> 
  filter(term == 'alpha') |> 
  pull(estimate)

initial_level <- pig_fit |> 
  tidy() |> 
  filter(term == 'l[0]') |> 
  pull(estimate)

cat('The optimal alpha value was calculated as:',alpha, '\n\n')
The optimal alpha value was calculated as: 0.3221247 
cat('The optimal inital level was calculates as:', initial_level, '\n\n')
The optimal inital level was calculates as: 100646.6 
report(pig_fit)
Series: Count 
Model: ETS(A,N,N) 
  Smoothing parameters:
    alpha = 0.3221247 

  Initial states:
     l[0]
 100646.6

  sigma^2:  87480760

     AIC     AICc      BIC 
13737.10 13737.14 13750.07 

A plot of the pig data and the corresponding forecast.

pig_forecast |> 
  autoplot(pigs) +
  geom_line(aes(y = .fitted), col = 'orange',
  data = augment(pig_fit)) +
  guides(colour = 'none')

b.

The prediction interval is calculated in the code below.

first <- pig_forecast |> 
  as_tibble() |> 
  slice(1) |> 
  pull(.mean)

s <- pig_fit |> 
  augment() |> 
  pull(.resid) |> 
  sd(na.rm = TRUE)

lower <- first - 1.96 * s
upper <- first + 1.96 * s

The following code uses r to calculate the prediction interval and then compares it to the manually calculated one from the code just above.

interval <- pig_forecast |>  
  hilo(level = 95) |> 
  as_tibble() |> 
  mutate(
    lower95 = `95%`$lower,
    upper95 = `95%`$upper) |> 
  slice(1)

lower_r <- interval$lower95
upper_r <- interval$upper95

cat('The manually calculated lower bound of the 95% interval:', lower, '\n')
The manually calculated lower bound of the 95% interval: 76871.01 
cat('The model calculated lower bound of the 95% interval:', lower_r, '\n')
The model calculated lower bound of the 95% interval: 76854.79 
cat('A difference of', lower - lower_r, '\n\n')
A difference of 16.22359 
cat('The manually calculated upper bound of the 95% interval:', upper, '\n')
The manually calculated upper bound of the 95% interval: 113502.1 
cat('The model calculated upper bound of the 95% interval:', upper_r, '\n')
The model calculated upper bound of the 95% interval: 113518.3 
cat('A difference of', upper_r - upper, '\n\n')
A difference of 16.22359 

As shown above the manually calculated and model calculated are close but differ by a value of 16.22359 for each bound.

8.5

a.

A plot of Mexico’s exports as a percentage of GDP over time.

mexports <- global_economy |> 
  filter(Country == 'Mexico') |> select(Country, Year, Exports)

mexports |> autoplot(Exports)

The plot of Mexico’s exports by year since 1960 shows an upward trend over time starting about 1975. There is ongoing variation throughout this trend with a notable decline and subsequent recovery in the mid 1990s.

b.

The code fits a simple exponential smoothing model to the Mexican exports data and forecasts the following ten years.

mexports_fit <- mexports |> 
  model(ses = ETS(Exports ~ error('A') + trend('N') + season('N')))

mexports_forecast <- mexports_fit |> 
  forecast(h = 10)

mexports_forecast |> autoplot(mexports) +
  geom_line(aes(y = .fitted), col = 'orange',
            data = augment(mexports_fit)) +
  guides(colour = 'none')

c.

The RMSE for the forecast is calculated in the following code and is then compared with the r calculated RMSE, which is calculated in the code block following the one below.

rmse_simple <- mexports_fit |> augment() |> 
  as_tibble() |> 
  group_by(Country, .model) |> 
  summarise(
    rmse = sqrt(mean(.resid^2)),
    .groups ='drop'
  ) |> pull(rmse)

cat(rmse_simple)
2.154425

The RMSE calculated above matches the one returned by the accuracy() function below.

mexports_fit |> accuracy()
# A tibble: 1 × 11
  Country .model .type       ME  RMSE   MAE   MPE  MAPE  MASE RMSSE  ACF1
  <fct>   <chr>  <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Mexico  ses    Training 0.506  2.15  1.38  1.83  7.78 0.983 0.991 0.203

d.

The Mexican export data is then fit to a model with a trend component.

mexports_fit_trend <- mexports |> 
  model(trend = ETS(Exports ~ error('A') + trend('A') + season('N')))

mexports_forecast_trend <- mexports_fit_trend |> 
  forecast(h = 10)

mexports_forecast_trend |> autoplot(mexports) +
  geom_line(aes(y = .fitted), col = 'orange',
            data = augment(mexports_fit_trend)) +
  guides(colour = 'none')

The model, with the additional parameter for trend, has a slightly improved RMSE over the model without trend, from a 2.154425 to 2.093999.

rmse_trend <- mexports_fit_trend |> augment() |> 
  as_tibble() |> 
  group_by(Country, .model) |> 
  summarise(
    rmse = sqrt(mean(.resid^2)),
    .groups ='drop'
  ) |> 
  pull(rmse)

cat(rmse_trend)
2.093999
mexports_fit_trend |> accuracy()
# A tibble: 1 × 11
  Country .model .type          ME  RMSE   MAE   MPE  MAPE  MASE RMSSE  ACF1
  <fct>   <chr>  <chr>       <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 Mexico  trend  Training -0.00816  2.09  1.41 -1.97  8.68  1.01 0.964 0.203

Ultimately, when considering the data for the exports, there is a clear trend upwards. With this in mind, utilizing the model that includes a trend component will most likely be beneficial when forecasting this data.

e.

The code below builds a table that calculates the difference between the two models forecasts.

mexports_forecast |> 
  as_tibble() |> 
  left_join(
    mexports_forecast_trend |> 
      as_tibble() |> 
      transmute(Country, Year, mean_trend = .mean),
    by = c('Country', 'Year')
    ) |> 
  mutate(
    trend_mean_difference = mean_trend - .mean
  )
# A tibble: 10 × 7
   Country .model  Year
   <fct>   <chr>  <dbl>
 1 Mexico  ses     2018
 2 Mexico  ses     2019
 3 Mexico  ses     2020
 4 Mexico  ses     2021
 5 Mexico  ses     2022
 6 Mexico  ses     2023
 7 Mexico  ses     2024
 8 Mexico  ses     2025
 9 Mexico  ses     2026
10 Mexico  ses     2027
# ℹ 4 more variables: Exports <dist>, .mean <dbl>, mean_trend <dbl>,
#   trend_mean_difference <dbl>

Inspecting the forecasts, the SES forecast is similar to a Naive forecast with each forecast equal to the last estimated level, whereas the trend forecast increases over time. Considering the noticeable trend in the observed data, the trended forecast is most likely the one that would be best to use in this instance.

f.

The code below manually calculates the prediction interval for the simple exponential smoothing model (without trend).

mean_mex_simple <- mexports_forecast |> 
  as_tibble() |> 
  slice(1) |> 
  pull(.mean)

mex_simple_lower <- mean_mex_simple - 1.96 * rmse_simple
mex_simple_upper <- mean_mex_simple + 1.96 * rmse_simple

cat(mex_simple_lower, '\n')
33.64367 
cat(mex_simple_upper)
42.08901

Then the prediction interval is manually calculated for the model with a trend component.

mean_mex_trend <- mexports_forecast_trend |> 
  as_tibble() |> 
  slice(1) |> 
  pull(.mean)

mex_trend_lower <- mean_mex_trend - 1.96 * rmse_trend
mex_trend_upper <- mean_mex_trend + 1.96 * rmse_trend

cat(mex_trend_lower, '\n')
34.278 
cat(mex_trend_upper)
42.48647

The prediction interval for the simple model is calculated using the R functions below.

mexport_interval <- mexports_forecast |>  
  hilo(level = 95) |> 
  as_tibble() |> 
  mutate(
    lower95 = `95%`$lower,
    upper95 = `95%`$upper) |> 
  slice(1)

mexports_lower_r <- mexport_interval$lower95
mexports_upper_r <- mexport_interval$upper95

cat(mexports_lower_r, '\n')
33.569 
cat(mexports_upper_r)
42.16368

The prediction interval for the trended model is calculated using the R functions below.

mexport_trend_interval <- mexports_forecast_trend |>  
  hilo(level = 95) |> 
  as_tibble() |> 
  mutate(
    lower95 = `95%`$lower,
    upper95 = `95%`$upper) |> 
  slice(1)

mexports_trend_lower_r <- mexport_trend_interval$lower95
mexports_trend_upper_r <- mexport_trend_interval$upper95

cat(mexports_trend_lower_r, '\n')
34.12878 
cat(mexports_trend_upper_r)
42.63569

The table below combines all the prediction intervals for comparison.

interval_table <- tibble(
  model = c('Simple exponential smoothing', 'Trend model'),
  manual_lower = c(mex_simple_lower, mex_trend_lower),
  manual_upper = c(mex_simple_upper, mex_trend_upper),
  r_lower = c(mexports_lower_r, mexports_trend_lower_r),
  r_upper = c(mexports_upper_r, mexports_trend_upper_r)
)

interval_table
# A tibble: 2 × 5
  model                        manual_lower manual_upper r_lower r_upper
  <chr>                               <dbl>        <dbl>   <dbl>   <dbl>
1 Simple exponential smoothing         33.6         42.1    33.6    42.2
2 Trend model                          34.3         42.5    34.1    42.6

Again, as in the previous problem, the ranges of the prediction intervals are very similar for the manually calculated and r-calculated ranges, however they are not identical.

8.6

Below is a plot for the GDP of China’s economy over time from 1960 to 2018.

china <- global_economy |>
  filter(Country == 'China') |> 
  select(Country, Year, GDP)
autoplot(china, GDP)

China’s GDP shows a strong growth trend.

Below, China’s GDP data is fit to four different models and plotted.

lambda <- china |> 
  features(GDP, guerrero) |> 
  pull(lambda_guerrero)

cat('lambda:', lambda)
lambda: -0.03446284
china_fit <- china |> 
  model(
    ses = ETS(GDP ~ error('A') + trend('N') + season('N')),
    trend = ETS(GDP ~ error('A') + trend('A') + season('N')),
    damped = ETS(GDP ~ error('A') + trend('Ad') + season('N')),
    box_cox = ETS(box_cox(GDP, lambda))
    )

china_forecast <- china_fit |> 
  forecast(h = 20)

china_forecast |> 
  autoplot(china)

Below is table of the different models’ forecasts.

china_forecast |> 
  as_tibble() |> 
  select(.model, Year, .mean) |> 
  pivot_wider(
    names_from = .model,
    values_from = .mean
  ) |> head()
# A tibble: 6 × 5
   Year     ses   trend  damped box_cox
  <dbl>   <dbl>   <dbl>   <dbl>   <dbl>
1  2018 1.22e13 1.30e13 1.30e13 1.39e13
2  2019 1.22e13 1.38e13 1.37e13 1.58e13
3  2020 1.22e13 1.45e13 1.44e13 1.81e13
4  2021 1.22e13 1.53e13 1.51e13 2.06e13
5  2022 1.22e13 1.60e13 1.58e13 2.36e13
6  2023 1.22e13 1.68e13 1.65e13 2.71e13

The models show the different behaviors of their forecasts. Simple exponential smoothing behaves as Naive forecasting, continuing to forecast the final estimated value, while a model including a trend component increases progressively over time. The damped trend also increases but the rate of increases is damped over time. As seen above the trend forecasts increase at a faster rate than the damped forecasts. The Box-Cox transformation forecast increases the forecast for this model at the fastest rate. The Box-Cox model changes the scale on which ETS models the growth and variation and then the transformed forecasts are returned to the GDP scale. The strong increasing trend of the observed China GDP data, supports the idea of the trend models all having continued growth rates.

8.7

Gas production data for Australia is plotted below.

gas <- aus_production |> 
  select(Quarter, Gas)

gas |> autoplot(Gas)

Looking at the plot of the Australian gas production shows quarterly gas production with a strong upward trend and a seasonal variation component.

The data is fit to seven different models and then each is plotted below.

lambda <- gas |> 
  features(Gas, guerrero) |> 
  pull(lambda_guerrero)

gas_fit <- gas |> 
  model(
    additive = ETS(Gas ~ error('A') + trend('A') + season('A')),
    multiplicative = ETS(Gas ~ error('M') + trend('M') + season('M')),
    additive_damped = ETS(Gas ~ error('A') + trend('Ad') + season('A')),
    multiplicative_damped = ETS(Gas ~ error('M') + trend('Md') + season('M')),
    multiplicative_additive_damped = ETS(Gas ~ error('M') + trend('Ad') + season('M')),
    box_cox = ETS(box_cox(Gas, lambda)),
    automatic = ETS((Gas))
  )

gas_fit |> 
  select(additive) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(additive) |> 
  report()
Series: Gas 
Model: ETS(A,A,A) 
  Smoothing parameters:
    alpha = 0.5209159 
    beta  = 0.0001000024 
    gamma = 0.4180453 

  Initial states:
     l[0]      b[0]      s[0]    s[-1]     s[-2]     s[-3]
 10.06526 0.9870514 -5.890283 15.61932 -2.811314 -6.917728

  sigma^2:  23.5601

     AIC     AICc      BIC 
1872.452 1873.318 1902.913 
gas_fit |> 
  select(multiplicative) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(multiplicative) |> 
  report()
Series: Gas 
Model: ETS(M,M,M) 
  Smoothing parameters:
    alpha = 0.6947632 
    beta  = 0.1182963 
    gamma = 0.08784581 

  Initial states:
     l[0]     b[0]      s[0]    s[-1]    s[-2]     s[-3]
 5.913798 1.010604 0.9320972 1.180305 1.070716 0.8168816

  sigma^2:  0.0032

     AIC     AICc      BIC 
1680.654 1681.519 1711.115 
gas_fit |> 
  select(additive_damped) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(additive_damped) |> 
  report()
Series: Gas 
Model: ETS(A,Ad,A) 
  Smoothing parameters:
    alpha = 0.5759917 
    beta  = 0.04513765 
    gamma = 0.3538354 
    phi   = 0.9558689 

  Initial states:
     l[0]      b[0]       s[0]    s[-1]    s[-2]     s[-3]
 16.69437 0.2260366 -0.7505321 2.699016 2.663774 -4.612259

  sigma^2:  21.8606

     AIC     AICc      BIC 
1857.090 1858.153 1890.935 
gas_fit |> 
  select(multiplicative_damped) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(multiplicative_damped) |> 
  report()
Series: Gas 
Model: ETS(M,Md,M) 
  Smoothing parameters:
    alpha = 0.6418229 
    beta  = 0.1296012 
    gamma = 0.0939101 
    phi   = 0.9615711 

  Initial states:
     l[0]     b[0]      s[0]    s[-1]    s[-2]    s[-3]
 5.853198 1.013851 0.9292935 1.183223 1.069365 0.818119

  sigma^2:  0.0032

     AIC     AICc      BIC 
1679.596 1680.658 1713.440 
gas_fit |> 
  select(multiplicative_additive_damped) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(multiplicative_damped) |> 
  report()
Series: Gas 
Model: ETS(M,Md,M) 
  Smoothing parameters:
    alpha = 0.6418229 
    beta  = 0.1296012 
    gamma = 0.0939101 
    phi   = 0.9615711 

  Initial states:
     l[0]     b[0]      s[0]    s[-1]    s[-2]    s[-3]
 5.853198 1.013851 0.9292935 1.183223 1.069365 0.818119

  sigma^2:  0.0032

     AIC     AICc      BIC 
1679.596 1680.658 1713.440 
gas_fit |> 
  select(box_cox) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(box_cox) |> 
  report()
Series: Gas 
Model: ETS(A,A,A) 
Transformation: box_cox(Gas, lambda) 
  Smoothing parameters:
    alpha = 0.81796 
    beta  = 0.1216283 
    gamma = 0.0001000577 

  Initial states:
     l[0]       b[0]       s[0]     s[-1]     s[-2]      s[-3]
 1.888944 0.03198818 -0.1065298 0.2508721 0.1003452 -0.2446875

  sigma^2:  0.0059

     AIC     AICc      BIC 
63.28945 64.15484 93.74991 
gas_fit |> 
  select(automatic) |> 
  forecast(h = 40) |> 
  autoplot(gas, level = NULL)

gas_fit |> 
  select(automatic) |> 
  report()
Series: Gas 
Model: ETS(M,A,M) 
Transformation: (Gas) 
  Smoothing parameters:
    alpha = 0.6528545 
    beta  = 0.1441675 
    gamma = 0.09784922 

  Initial states:
     l[0]       b[0]      s[0]    s[-1]    s[-2]     s[-3]
 5.945592 0.07062881 0.9309236 1.177883 1.074851 0.8163427

  sigma^2:  0.0032

     AIC     AICc      BIC 
1680.929 1681.794 1711.389 

This table shows the report data for each of the seven models plotted above.

glance(gas_fit) |> 
  arrange(AICc)
# A tibble: 7 × 9
  .model              sigma2 log_lik    AIC   AICc    BIC     MSE    AMSE    MAE
  <chr>                <dbl>   <dbl>  <dbl>  <dbl>  <dbl>   <dbl>   <dbl>  <dbl>
1 box_cox            5.86e-3   -22.6   63.3   64.2   93.7 5.65e-3  0.0102 0.0570
2 multiplicative_da… 3.21e-3  -830.  1680.  1681.  1713.  2.08e+1 31.5    0.0410
3 multiplicative     3.21e-3  -831.  1681.  1682.  1711.  2.20e+1 33.9    0.0413
4 automatic          3.24e-3  -831.  1681.  1682.  1711.  2.11e+1 32.2    0.0413
5 multiplicative_ad… 3.29e-3  -832.  1684.  1685.  1718.  2.11e+1 32.0    0.0417
6 additive_damped    2.19e+1  -919.  1857.  1858.  1891.  2.10e+1 29.6    3.16  
7 additive           2.36e+1  -927.  1872.  1873.  1903.  2.27e+1 29.7    3.35  

Inspecting the individual models and their corresponding AICc scores, it appears that a multiplicative-damped model ETS(M, Md, M) provides the preferred model, followed closely by the fully multiplicative, and then an ETS(M, A, M) model. The small difference provides little evidence to prefer one over the other. The Box-Cox model cannot be directly compared as it underwent transformation and is fitted on a transformed scale.

The seasonality component is necessarily multiplicative because the size of the seasonal fluctuations increases as the level of gas production rises. It models the seasonal effects proportionally to the level. Additive seasonality assumes fluctuations are roughly constant in size.

The damped trend model improves the additive and multiplicative model’s AICc score. While there is a relatively small difference in AICc values, results do support damping the multiplicative trend.

8.8

The retail data is plotted below.

# set the seed
set.seed(47)

# select the retail data
myseries <- aus_retail |> filter(`Series ID` == sample(aus_retail$`Series ID`, 1))

# plot the training data and the entire data
autoplot(myseries, Turnover)

# create a training dataset of observations before 2010
myseries_train <- myseries |> filter(year(Month) <= 2010)

# plot the training data and the entire data
autoplot(myseries, Turnover) +
  autolayer(myseries_train, Turnover, colour = "red")

a.

Inspecting the plot of the retail data series, multiplicative seasonality is necessary because the magnitude of the seasonality increases with the level of the series. A multiplicative seasonal component allows the forecast to adjust proportional to the level of the data series.

b.

Two models are fit to the retail data below and then both are plotted.

lambda <- myseries |> 
  features(Turnover, guerrero) |> 
  pull(lambda_guerrero)

myseries_fit <- myseries |> 
  model(
    ets_mult = ETS(Turnover ~ error('M') + trend('A') + season('M')),
    ets_damped = ETS(Turnover ~ error('M') + trend('Ad') + season('M'))
  )

myseries_fit |>
  select(State, Industry, ets_mult) |> 
  forecast(h = 36) |>
  autoplot(myseries, level = NULL)

myseries_fit |> 
  select(ets_mult) |> 
  report()
Series: Turnover 
Model: ETS(M,A,M) 
  Smoothing parameters:
    alpha = 0.3891239 
    beta  = 0.0001001378 
    gamma = 0.1833412 

  Initial states:
     l[0]      b[0]      s[0]     s[-1]     s[-2]    s[-3]    s[-4]    s[-5]
 45.40407 0.7237567 0.8641859 0.7649106 0.9353169 1.450089 1.047977 1.036834
    s[-6]     s[-7]     s[-8]     s[-9]   s[-10]    s[-11]
 1.027067 0.9674972 0.9716836 0.9911705 1.011353 0.9319146

  sigma^2:  0.0025

     AIC     AICc      BIC 
4597.666 4599.112 4667.179 
myseries_fit |>
  select(State, Industry, ets_damped) |> 
  forecast(h = 36) |>
  autoplot(myseries, level = NULL)

myseries_fit |> 
  select(ets_damped) |> 
  report()
Series: Turnover 
Model: ETS(M,Ad,M) 
  Smoothing parameters:
    alpha = 0.4848494 
    beta  = 0.01207443 
    gamma = 0.1545561 
    phi   = 0.9799992 

  Initial states:
     l[0]     b[0]      s[0]     s[-1]     s[-2]    s[-3]    s[-4]    s[-5]
 44.99204 0.338861 0.8642028 0.7674952 0.9394509 1.453263 1.064383 1.044351
    s[-6]     s[-7]     s[-8]     s[-9]   s[-10]    s[-11]
 1.014907 0.9744656 0.9820932 0.9772068 1.008291 0.9098907

  sigma^2:  0.0026

     AIC     AICc      BIC 
4605.481 4607.102 4679.084 
report(myseries_fit)
# A tibble: 2 × 11
  State     Industry .model  sigma2 log_lik   AIC  AICc   BIC   MSE  AMSE    MAE
  <chr>     <chr>    <chr>    <dbl>   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>  <dbl>
1 Queensla… Clothin… ets_m… 0.00248  -2282. 4598. 4599. 4667.  123.  151. 0.0378
2 Queensla… Clothin… ets_d… 0.00255  -2285. 4605. 4607. 4679.  128.  163. 0.0380

Inspecting the results of the models, they are all very similar. The non-damped version is the preferred version based on the AICc. Ultimately, the difference is small enough that there is not a compelling reason to select one model over the other.

c.

myseries_fit |> 
  accuracy() |> 
  select(.model, RMSE)
# A tibble: 2 × 2
  .model      RMSE
  <chr>      <dbl>
1 ets_mult    11.1
2 ets_damped  11.3

The RMSE for both models are also relatively similar. The Holt-Winters undamped model has a slightly lower value make it preferred in this instance but the difference to the damped version is minimal.

d.

myseries_fit |>
  select(ets_mult) |> 
  augment() |> 
  autoplot(.resid)

The residuals are plotted above. The residual variation does not appear exclusively as white noise. The magnitude of the variation increases along with the increase in level of the series data but there is no discernible pattern within the residuals.

myseries_fit |> 
  select(ets_mult) |> 
  gg_tsresiduals()

The innovation residuals fluctuate around zero with roughly constant variance. Some of the spikes exceed the dashed bounds, especially at a 12 month lag, indicating that the model has not captured all of the seasonality and showing substantial autocorrelation. While they have a bell shaped distribution, the residuals do not fully resemble white noise.

e.

The data is split into a training and test set in the code below. Then an ETS(M, A, M) model is fit to the training data and forecasted to the test data.

# create a training dataset of observations before 2011 and a test set from 2011 on
myseries_train <- myseries |> filter(year(Month) <= 2010) 

myseries_test <- myseries |> filter(year(Month) > 2010)

train_fit <- myseries_train |> 
  model(
    ets_mult_train = ETS(Turnover ~ error('M') + trend('A') + season('M')))

train_forecast <- train_fit |> 
  forecast(new_data = myseries_test)
  
train_forecast |> 
  autoplot(myseries_train, level = NULL)

Below the RMSE for the is calculated below.

train_forecast |> 
  as_tibble() |> 
  select(State, Industry, .model, Month, .mean) |> 
  left_join(
    myseries_test |>
      as_tibble() |> 
      select(State, Industry, Month, Turnover),
    by = c('State', 'Industry','Month')
  ) |> group_by(State, Industry, .model) |> 
  summarise(
    RMSE = sqrt(mean((Turnover - .mean)^2)),
    .groups = 'drop'
  )
# A tibble: 1 × 4
  State      Industry                                            .model     RMSE
  <chr>      <chr>                                               <chr>     <dbl>
1 Queensland Clothing, footwear and personal accessory retailing ets_mult…  28.3

A seasonal naive model is fitted to the training data below and then a forecast is made and the RMSE calculated.

# fit a seasonal naive model to the training data
fit <- myseries_train |> model(SNAIVE(Turnover))

forecast <- fit |> 
  forecast(myseries_test)

forecast |> 
  as_tibble() |> 
  select(State, Industry, .model, Month, .mean) |> 
  left_join(
    myseries_test |>
      as_tibble() |> 
      select(State, Industry, Month, Turnover),
    by = c('State', 'Industry','Month')
  ) |> group_by(State, Industry, .model) |> 
  summarise(
    RMSE = sqrt(mean((Turnover - .mean)^2)),
    .groups = 'drop'
  ) |> select(RMSE)
# A tibble: 1 × 1
   RMSE
  <dbl>
1  52.3

The RMSE from the Holt-Winters model has an RMSE of 28.25067. The seasonal naive model has an RMSE of 52.28899. The Holt-Winters model reduced the RMSE substantially from the seasonal naive model.

8.9

The retail data is plotted again below.

myseries
# A tsibble: 441 x 5 [1M]
# Key:       State, Industry [1]
   State      Industry                             `Series ID`    Month Turnover
   <chr>      <chr>                                <chr>          <mth>    <dbl>
 1 Queensland Clothing, footwear and personal acc… A3349884J   1982 Apr     42.8
 2 Queensland Clothing, footwear and personal acc… A3349884J   1982 May     45.3
 3 Queensland Clothing, footwear and personal acc… A3349884J   1982 Jun     45.1
 4 Queensland Clothing, footwear and personal acc… A3349884J   1982 Jul     43.1
 5 Queensland Clothing, footwear and personal acc… A3349884J   1982 Aug     41.5
 6 Queensland Clothing, footwear and personal acc… A3349884J   1982 Sep     44.5
 7 Queensland Clothing, footwear and personal acc… A3349884J   1982 Oct     48.6
 8 Queensland Clothing, footwear and personal acc… A3349884J   1982 Nov     51.4
 9 Queensland Clothing, footwear and personal acc… A3349884J   1982 Dec     70.9
10 Queensland Clothing, footwear and personal acc… A3349884J   1983 Jan     44.6
# ℹ 431 more rows
autoplot(myseries, Turnover) +
  autolayer(myseries_train, Turnover, colour = "red")

A model is fit to the data below consisting of applying an ETS model to an STL decomposition of the Box-Cox transformation of the retail data.

lambda <- myseries_train |> 
  features(Turnover, guerrero) |> 
  pull(lambda_guerrero)

stl_fit <-myseries_train |> 
  model(
    stl_ets = decomposition_model(
      STL(
        box_cox(Turnover, lambda) ~ season(window = 'periodic')
      ),
      ETS(season_adjust ~ season('N'))
    )
  )

stl_forecast <- stl_fit |> 
  forecast(new_data = myseries_test)

stl_forecast |> 
  accuracy(myseries_test) |> 
  select(.model, RMSE)
# A tibble: 1 × 2
  .model   RMSE
  <chr>   <dbl>
1 stl_ets  83.0

The model created from by applying an ETS model to a STL decomposition of the Box-Cox transformation of the data results in an RMSE of 82.97691. When compared with the 28.25 for the Holt-Winters model and the 52.29 seasonal naive, the ETS/STL/Box-Cox model performs worse than both of the alternative on this test set. From these three models, the undamped Holt-Winters is preferred.