Exercise: 8.1
Consider the the number of pigs slaughtered in Victoria, available
in the aus_livestock dataset.
(a)Use the ETS() function to estimate the equivalent model for
simple exponential smoothing. Find the optimal values of α and ℓ, and
generate forecasts for the next four months.
(b)Compute a 95% prediction interval for the first forecast using
y±1.96s where s is the standard deviation of the residuals. Compare your
interval with the interval produced by R.
(a)
Data set:
library(fpp3)
## Registered S3 method overwritten by 'tsibble':
## method from
## as_tibble.grouped_df dplyr
## ── Attaching packages ──────────────────────────────────────────── fpp3 1.0.2 ──
## ✔ tibble 3.2.1 ✔ tsibble 1.1.6
## ✔ dplyr 1.1.4 ✔ tsibbledata 0.4.1
## ✔ tidyr 1.3.1 ✔ feasts 0.4.2
## ✔ lubridate 1.9.4 ✔ fable 0.5.0
## ✔ ggplot2 3.5.2
## ── Conflicts ───────────────────────────────────────────────── fpp3_conflicts ──
## ✖ lubridate::date() masks base::date()
## ✖ dplyr::filter() masks stats::filter()
## ✖ tsibble::intersect() masks base::intersect()
## ✖ tsibble::interval() masks lubridate::interval()
## ✖ dplyr::lag() masks stats::lag()
## ✖ tsibble::setdiff() masks base::setdiff()
## ✖ tsibble::union() masks base::union()
aus_livestock
## # A tsibble: 29,364 x 4 [1M]
## # Key: Animal, State [54]
## Month Animal State Count
## <mth> <fct> <fct> <dbl>
## 1 1976 Jul Bulls, bullocks and steers Australian Capital Territory 2300
## 2 1976 Aug Bulls, bullocks and steers Australian Capital Territory 2100
## 3 1976 Sep Bulls, bullocks and steers Australian Capital Territory 2100
## 4 1976 Oct Bulls, bullocks and steers Australian Capital Territory 1900
## 5 1976 Nov Bulls, bullocks and steers Australian Capital Territory 2100
## 6 1976 Dec Bulls, bullocks and steers Australian Capital Territory 1800
## 7 1977 Jan Bulls, bullocks and steers Australian Capital Territory 1800
## 8 1977 Feb Bulls, bullocks and steers Australian Capital Territory 1900
## 9 1977 Mar Bulls, bullocks and steers Australian Capital Territory 2700
## 10 1977 Apr Bulls, bullocks and steers Australian Capital Territory 2300
## # ℹ 29,354 more rows
vic_pigs <- aus_livestock %>%
filter(Animal == "Pigs", State == "Victoria")
fit <- vic_pigs %>%
model(ETS(Count ~ error("A") + trend("N") + season("N")))
report(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
Forecast for the next 4 months:
fc <- fit %>% forecast(h = "4 months")
# Plot the forecasts
fc %>%
autoplot(vic_pigs, level = 95) + # Original data + 95% PI
labs(
title = "SES Forecast of Pigs Slaughtered in Victoria",
y = "Number of Pigs",
x = "Month"
) +
theme_minimal()

(b)
# Prediction interval formula: y_hat +/- 1.96 * sd
c <- 1.96
# Get residual sd (standard deviation)
resid_sd <- augment(fit) %>%
as_tibble() %>%
summarise(s = sd(.resid, na.rm = TRUE)) %>%
pull(s)
# first forecast mean
y_hat <- fc[1,] %>%
pull(.mean)
# calculate the prediction interval
PI_lower <- y_hat - (c * resid_sd)
PI_upper <- y_hat + (c * resid_sd)
manual_PI <- tibble(
Source = "Manual 95% Prediction Interval",
"Lower Bound" = PI_lower,
Mean = y_hat,
"Upper Bound" = PI_upper
)
# Calculate R's prediction interval
R_PI <- fc %>%
hilo(95) %>%
unpack_hilo(`95%`) %>%
as_tibble() %>%
slice(1)
R_PI_tibble <- tibble(
Source = "R 95% Prediction Interval",
"Lower Bound" = R_PI$`95%_lower`,
"Mean" = R_PI$.mean,
"Upper Bound" = R_PI$`95%_upper`
)
bind_rows(manual_PI, R_PI_tibble)
## # A tibble: 2 × 4
## Source `Lower Bound` Mean `Upper Bound`
## <chr> <dbl> <dbl> <dbl>
## 1 Manual 95% Prediction Interval 76871. 95187. 113502.
## 2 R 95% Prediction Interval 76855. 95187. 113518.
Exercise: 8.5
Data set global_economy contains the annual Exports from many
countries. Select one country to analyse.
(a) Plot the Exports series and discuss the main features of the
data.
(b) Use an ETS(A,N,N) model to forecast the series, and plot the
forecasts.
(c) Compute the RMSE values for the training data.
(d) Compare the results to those from an ETS(A,A,N) model. (Remember
that the trended model is using one more parameter than the simpler
model.) Discuss the merits of the two forecasting methods for this data
set.
(e) Compare the forecasts from both methods. Which do you think is
best?
(f) Calculate a 95% prediction interval for the first forecast for
each model, using the RMSE values and assuming normal errors. Compare
your intervals with those produced using R.
Answer(a)
Select Country Bangladesh:
ban_exports <- global_economy %>%
filter(Country == "Bangladesh")
ban_exports %>%
autoplot(Exports) +
labs(
title = "Exports of Bangladesh",
y = "Exports (% of GDP)",
x = "Year"
) +
theme_minimal()

(b)
fit <- ban_exports %>%
model(ETS(Exports ~ error("A") + trend("N") + season("N")))
fc <- fit %>%
forecast(h = "10 years")
fc %>%
autoplot(ban_exports) +
labs(
title = "Forecast of Bangladesh Exports using ETS(A,N,N)",
y = "Exports (% of GDP)",
x = "Year"
) +
theme_minimal()

Comment: The exports series for Bangladesh was forecast using an
ETS(A,N,N) model, which corresponds to simple exponential smoothing.
This model assumes additive errors with no trend or seasonality. The
forecasts were generated for the next ten years and plotted along with
the historical data. The forecast line remains relatively stable,
reflecting the model’s assumption that future values will fluctuate
around the estimated level of the series.
(c)
ban_exports <- global_economy %>%
filter(Country == "Bangladesh")
fit <- ban_exports %>%
model(ETS(Exports ~ error("A") + trend("N") + season("N")))
accuracy(fit)
## # 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 Bangladesh "ETS(Expo… Trai… 0.0869 1.25 0.945 -0.891 12.0 0.983 0.991 0.0148
(d)
Fit both models together
ban_exports <- global_economy %>%
filter(Country == "Bangladesh")
models <- ban_exports %>%
model(
SES = ETS(Exports ~ error("A") + trend("N") + season("N")),
Holt = ETS(Exports ~ error("A") + trend("A") + season("N"))
)
Compare accuracy in one table:
accuracy(models)
## # A tibble: 2 × 11
## Country .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Bangladesh SES Training 0.0869 1.25 0.945 -0.891 12.0 0.983 0.991 0.0148
## 2 Bangladesh Holt Training 0.00557 1.25 0.954 -1.90 12.2 0.993 0.989 0.0135
(e)
ban_exports <- global_economy %>%
filter(Country == "Bangladesh")
models <- ban_exports %>%
model(
SES = ETS(Exports ~ error("A") + trend("N") + season("N")),
Holt = ETS(Exports ~ error("A") + trend("A") + season("N"))
)
fc <- models %>%
forecast(h = "10 years")
fc %>%
autoplot(ban_exports) +
facet_wrap(~.model, ncol = 2) +
labs(
title = "Exports Forecast for Bangladesh",
subtitle = "Comparison of SES and Holt Models",
x = "Year",
y = "Exports (% of GDP)"
) +
theme_minimal()

Comment: The forecasts from the ETS(A,N,N) and ETS(A,A,N) models are
displayed in separate panels using facet plots. The SES model assumes no
trend and produces relatively stable forecasts, while the Holt model
includes an additive trend and therefore allows forecasts to increase or
decrease over time. Presenting the forecasts side-by-side makes it
easier to visually compare how the inclusion of a trend component
affects the forecast pattern.
(f)
fc <- models %>%
forecast(h = "10 years")
fc
## # A fable: 20 x 5 [1Y]
## # Key: Country, .model [2]
## Country .model Year
## <fct> <chr> <dbl>
## 1 Bangladesh SES 2018
## 2 Bangladesh SES 2019
## 3 Bangladesh SES 2020
## 4 Bangladesh SES 2021
## 5 Bangladesh SES 2022
## 6 Bangladesh SES 2023
## 7 Bangladesh SES 2024
## 8 Bangladesh SES 2025
## 9 Bangladesh SES 2026
## 10 Bangladesh SES 2027
## 11 Bangladesh Holt 2018
## 12 Bangladesh Holt 2019
## 13 Bangladesh Holt 2020
## 14 Bangladesh Holt 2021
## 15 Bangladesh Holt 2022
## 16 Bangladesh Holt 2023
## 17 Bangladesh Holt 2024
## 18 Bangladesh Holt 2025
## 19 Bangladesh Holt 2026
## 20 Bangladesh Holt 2027
## # ℹ 2 more variables: Exports <dist>, .mean <dbl>
accuracy(models)
## # A tibble: 2 × 11
## Country .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <fct> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Bangladesh SES Training 0.0869 1.25 0.945 -0.891 12.0 0.983 0.991 0.0148
## 2 Bangladesh Holt Training 0.00557 1.25 0.954 -1.90 12.2 0.993 0.989 0.0135
fc %>%
hilo(level = 95)
## # A tsibble: 20 x 6 [1Y]
## # Key: Country, .model [2]
## Country .model Year
## <fct> <chr> <dbl>
## 1 Bangladesh SES 2018
## 2 Bangladesh SES 2019
## 3 Bangladesh SES 2020
## 4 Bangladesh SES 2021
## 5 Bangladesh SES 2022
## 6 Bangladesh SES 2023
## 7 Bangladesh SES 2024
## 8 Bangladesh SES 2025
## 9 Bangladesh SES 2026
## 10 Bangladesh SES 2027
## 11 Bangladesh Holt 2018
## 12 Bangladesh Holt 2019
## 13 Bangladesh Holt 2020
## 14 Bangladesh Holt 2021
## 15 Bangladesh Holt 2022
## 16 Bangladesh Holt 2023
## 17 Bangladesh Holt 2024
## 18 Bangladesh Holt 2025
## 19 Bangladesh Holt 2026
## 20 Bangladesh Holt 2027
## # ℹ 3 more variables: Exports <dist>, .mean <dbl>, `95%` <hilo>
Comment: A 95% prediction interval for the first forecast can be
calculated using y±1.96×𝑅𝑀𝑆𝐸y ±1.96×RMSE, assuming normally
distributed errors. The RMSE from the training data is used as an
estimate of the residual standard deviation. After calculating the
intervals for both the ETS(A,N,N) and ETS(A,A,N) models, the results are
very close to the prediction intervals produced by R. Minor differences
may occur because R computes the intervals based on the full ETS model
variance, while the manual method is a simplified approximation.
Exercise: 8.6
Forecast the Chinese GDP from the global_economy data set using an
ETS model. Experiment with the various options in the ETS() function to
see how much the forecasts change with damped trend, or with a Box-Cox
transformation. Try to develop an intuition of what each is doing to the
forecasts.
[Hint: use a relatively large value of h when forecasting, so you
can clearly see the differences between the various options when
plotting the forecasts.]
china_gdp <- global_economy %>%
filter(Country == "China")
china_gdp %>%
autoplot(GDP) +
labs(
title = "GDP of China",
y = "GDP (US Dollars)",
x = "Year"
) +
theme_minimal()

Fit different ETS models:
china_models <- china_gdp %>%
model(
Standard = ETS(GDP),
Damped = ETS(GDP ~ trend("Ad")),
BoxCox = ETS(box_cox(GDP, 0))
)
Forecast far into the future with plot:
china_fc <- china_models %>%
forecast(h = 30)
china_fc %>%
autoplot(china_gdp) +
facet_wrap(~.model, scales = "free_y") +
labs(
title = "Forecasts of China's GDP using ETS Models",
x = "Year",
y = "GDP"
) +
theme_minimal()

Intuition behind the options:
Standard ETS
Forecast follows the existing trend strongly
Can produce very rapid growth if trend is strong.
Damped Trend
Trend gradually slows down over time
Forecast curve becomes flatter in the long run
Often more realistic for long-term economic forecasts.
Box-Cox Transformation
Reduces variance instability
Makes exponential growth appear more linear
Produces smoother forecasts with narrower intervals.
Comment: China’s GDP shows a strong upward trend over time. The
standard ETS model continues this rapid growth into the future. When a
damped trend is included, the growth rate gradually slows, producing
more conservative long-term forecasts. Applying a Box-Cox transformation
stabilizes the variance of the series and results in smoother forecasts.
Comparing these models helps illustrate how ETS options influence the
shape and uncertainty of long-term forecasts.
Exercise: 8.7
Find an ETS model for the Gas data from aus_production and forecast
the next few years. Why is multiplicative seasonality necessary here?
Experiment with making the trend damped. Does it improve the
forecasts?
Plot the gas data:
gas_data <- aus_production %>%
select(Quarter, Gas)
gas_data %>%
autoplot(Gas) +
labs(
title = "Australian Gas Production",
x = "Quarter",
y = "Gas Production"
) +
theme_minimal()

Fit an ETS model
gas_fit <- gas_data %>%
model(ETS(Gas))
report(gas_fit)
## Series: Gas
## Model: ETS(M,A,M)
## 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
Forecasting the next few years:
gas_fc <- gas_fit %>%
forecast(h = 20)
gas_fc %>%
autoplot(gas_data) +
labs(
title = "Forecasts of Australian Gas Production",
x = "Quarter",
y = "Gas Production"
)

Multiplicative seasonality is needed because the seasonal
fluctuations increase as the level of the series increases. In the Gas
data, the difference between seasonal peaks and troughs becomes larger
over time as production grows. A multiplicative seasonal model allows
the seasonal effect to scale with the level of the series, making it
more appropriate than an additive seasonal model.
Experiment with a damped trend:
# Fit another model with a damped trend.
gas_damped <- gas_data %>%
model(ETS(Gas ~ trend("Ad")))
report(gas_damped)
## Series: Gas
## Model: ETS(M,Ad,M)
## Smoothing parameters:
## alpha = 0.6489044
## beta = 0.1551275
## gamma = 0.09369372
## phi = 0.98
##
## Initial states:
## l[0] b[0] s[0] s[-1] s[-2] s[-3]
## 5.858941 0.09944006 0.9281912 1.177903 1.07678 0.8171255
##
## sigma^2: 0.0033
##
## AIC AICc BIC
## 1684.028 1685.091 1717.873
Forecast:
gas_damped_fc <- gas_damped %>%
forecast(h = 20)
gas_damped_fc %>%
autoplot(gas_data)

Comparing forecast accuray:
accuracy(gas_fit)
## # A tibble: 1 × 10
## .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 ETS(Gas) Training -0.115 4.60 3.02 0.199 4.08 0.542 0.606 -0.0131
accuracy(gas_damped)
## # A tibble: 1 × 10
## .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 "ETS(Gas ~ trend(\… Trai… -0.00439 4.59 3.03 0.326 4.10 0.544 0.606 -0.0217
Comment: An ETS model was fitted to the Gas production data from the
aus_production dataset. The selected model includes multiplicative
seasonality because the seasonal fluctuations increase as the level of
the series grows. Forecasts for the next few years show continued growth
in gas production with strong seasonal patterns. When a damped trend
model was tested, the long-term forecasts became flatter, but the
improvement in forecast accuracy was small. Therefore, the standard ETS
model with multiplicative seasonality remains appropriate for this
data.
The damped trend slightly reduces long-term growth in the forecasts,
but the accuracy measures are very similar to the non-damped model,
indicating that it does not significantly improve the forecasting
performance.
Expercise : 8.8
Recall your retail time series data (from Exercise 7 in Section
2.10).
(a) Why is multiplicative seasonality necessary for this
series?
(b) Apply Holt-Winters’ multiplicative method to the data.
Experiment with making the trend damped.
(c) Compare the RMSE of the one-step forecasts from the two methods.
Which do you prefer?
(d) Check that the residuals from the best method look like white
noise.
(e) Now find the test set RMSE, while training the model to the end
of 2010. Can you beat the seasonal naïve approach from Exercise 7 in
Section 5.11?
Time series data:
takeaway <- aus_retail %>%
filter(Industry == "Takeaway food services") %>%
summarise(Turnover = sum(Turnover))
takeaway %>%
autoplot(Turnover) +
labs(
title = "Australian Takeaway Food Turnover (Total)",
y = "Turnover (AUD)",
x = "Month"
)

(a)
Multiplicative seasonality is required because the size of the
seasonal fluctuations increases as the level of the series increases. In
the retail data, the seasonal peaks and troughs become larger over time
as sales grow. This means the seasonal effect is proportional to the
level of the series, making a multiplicative seasonal model more
appropriate than an additive one.
(b)
# Apply Holt-Winters multiplicative method
takeaway_fit <- takeaway %>%
model(
HW = ETS(Turnover ~ error("M") + trend("A") + season("M")),
HW_damped = ETS(Turnover ~ error("M") + trend("Ad") + season("M"))
)
# Forecast the next few years
takeaway_fc <- takeaway_fit %>%
forecast(h = "3 years")
takeaway_fc %>%
autoplot(takeaway) +
labs(
title = "Takeaway Food Turnover Forecasts",
y = "Turnover (AUD)",
x = "Month"
)

Compare accuracy:
accuracy(takeaway_fit)
## # A tibble: 2 × 10
## .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HW Training 0.0873 18.6 13.7 -0.0673 1.96 0.308 0.329 0.172
## 2 HW_damped Training 2.02 18.6 13.7 0.259 1.95 0.309 0.329 0.160
(c)
The RMSE for the Holt-Winters multiplicative model is 18.58, while
the damped trend model has an RMSE of 18.57. Since the damped model has
a slightly lower RMSE, it provides marginally better one-step-ahead
forecasts. However, the difference is very small, so both models perform
similarly.
(d)
takeaway_fit %>%
select(HW_damped) %>%
gg_tsresiduals()
## Warning: `gg_tsresiduals()` was deprecated in feasts 0.4.2.
## ℹ Please use `ggtime::gg_tsresiduals()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_lifecycle_warnings()` to see where this warning was
## generated.

Comment: The residual diagnostics for the damped Holt-Winters model
show that the residuals fluctuate randomly around zero and exhibit no
significant autocorrelation. Therefore, the residuals resemble white
noise, indicating that the model provides an adequate fit to the
data.
(e)
# Training and test data
train <- takeaway %>%
filter_index(. ~ "2010 Dec")
test <- takeaway %>%
filter_index("2011 Jan" ~ .)
# Fit the Holt-Winters model on training data
hw_model <- train %>%
model(
HW_damped = ETS(Turnover ~ error("M") + trend("Ad") + season("M"))
)
# Forecast the test period
hw_fc <- hw_model %>%
forecast(new_data = test)
# Compute test RMSE
accuracy(hw_fc, test)
## # A tibble: 1 × 10
## .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 HW_damped Test 20.8 105. 87.8 0.714 6.30 NaN NaN 0.916
# Seasonal naïve model
naive_model <- train %>%
model(SNAIVE(Turnover))
naive_fc <- naive_model %>%
forecast(new_data = test)
accuracy(naive_fc, test)
## # A tibble: 1 × 10
## .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 SNAIVE(Turnover) Test 175. 228. 180. 11.9 12.4 NaN NaN 0.954
Comment: The Holt-Winters damped model has a test RMSE of 105.1,
which is much lower than the seasonal naïve RMSE of 228.0. Therefore,
the Holt-Winters model provides more accurate forecasts and clearly
beats the seasonal naïve approach.
Exercise: 8.9
Comment: The STL + ETS model produced a test RMSE of 259.91, which
is higher than both the Holt-Winters damped model (105.1) and the
seasonal naïve model (228.0). Therefore, the Holt-Winters damped model
provides the best forecasts for this dataset.
Comment: The exports series for Bangladesh was plotted using the autoplot() function. Since the data is annual, there is no seasonal pattern present in the series. The exports values fluctuate over time with moderate variability. From the plot, exports as a percentage of GDP generally show an increasing trend after the year 2000, indicating growth in Bangladesh’s export sector. However, around 2020 there is a noticeable decrease, which may reflect economic disruptions or changes in global trade conditions.