Consider the the number of pigs slaughtered in Victoria, available in the aus_livestock dataset.
head(aus_livestock)
## # A tsibble: 6 x 4 [1M]
## # Key: Animal, State [1]
## 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
x <- aus_livestock %>%
filter(Animal=="Pigs", State=="Victoria")
x %>% autoplot(Count)
fit <- x %>% model(ses=ETS(Count~error("A")+trend("N") +season("N")))
report <- fit %>% report()
## 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
We can see a = 0.3221247 and ℓ0 = 100646.6
Now I will generate forecasts for the next four months
forc <- fit %>% forecast(h="4 months")
forc
## # A fable: 4 x 6 [1M]
## # Key: Animal, State, .model [1]
## Animal State .model Month Count .mean
## <fct> <fct> <chr> <mth> <dist> <dbl>
## 1 Pigs Victoria ses 2019 Jan N(95187, 8.7e+07) 95187.
## 2 Pigs Victoria ses 2019 Feb N(95187, 9.7e+07) 95187.
## 3 Pigs Victoria ses 2019 Mar N(95187, 1.1e+08) 95187.
## 4 Pigs Victoria ses 2019 Apr N(95187, 1.1e+08) 95187.
For the above table we can see the forcast for the next four months.
Lets plots with the last 8 months.
forc %>%
autoplot(filter(aus_livestock, Month >= yearmonth("2018 May"))) +
labs(title="forecast")
s <- augment(fit) %>% pull(.resid) %>%
sd()
yhat <- forc %>%
pull(Count) %>%
head(1)
yhat + c(-1,1) * 1.96 *s
## <distribution[2]>
## [1] N(76871, 8.7e+07) N(113502, 8.7e+07)
The prediction interval is 76871≤ŷ≤113502.
Now I will generate the prediction interval produced by R
forc %>%
mutate(interval = hilo(Count, 95)) %>%
pull(interval)
## <hilo[4]>
## [1] [76854.79, 113518.3]95 [75927.17, 114445.9]95 [75042.22, 115330.9]95
## [4] [74194.54, 116178.6]95
The itnerval look close to each other, because the R cacluated teh variance of the residuals in a different way.
Data set global_economy contains the annual Exports from many countries. Select one country to analyse.
BEL <- global_economy %>% filter(Code=="BEL")
BEL %>% autoplot(Exports)
It looks the exports has a upward trend.
fit <- BEL %>%
model(AMM = ETS(Exports~ error("A") + trend("N") + season ("N")))
forc <- fit %>% forecast(h=4)
forc %>% autoplot(BEL)
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 Belgium AMM Training 0.877 3.23 2.52 1.35 4.30 0.987 0.989 -0.0642
The RMSE value is 3.227668.
fit_compare <- BEL %>%
model(ANN = ETS(Exports ~ error("A") + trend ("N") + season("N")),
AAN = ETS(Exports ~ error("A") + trend ("N") + season("N")))
accuracy(fit_compare)
## # 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 Belgium ANN Training 0.877 3.23 2.52 1.35 4.30 0.987 0.989 -0.0642
## 2 Belgium AAN Training 0.877 3.23 2.52 1.35 4.30 0.987 0.989 -0.0642
AAN reduces the RMSE value, which results into better accuracy.
fit_compare %>%
forecast(h=4) %>%
autoplot(BEL, level = NULL)
AAN model is higher than ANN model because AAN model allows increasing trend, so AAN model is the best model for trend models.
Using AAN because its better I think.
s <- fit_compare %>%
select(Country, AAN) %>%
accuracy() %>%
transmute(Country, s = RMSE)
fit_compare %>%
select(Country, AAN) %>%
forecast(h=1) %>%
left_join(s, by = "Country") %>%
mutate( low = Exports - 1.96 * s,
high = Exports + 1.96 * s) %>%
select(Country, Exports, low, high)
## # A fable: 1 x 5 [?]
## # Key: Country [1]
## Country Exports low high Year
## <fct> <dist> <dist> <dist> <dbl>
## 1 Belgium N(85, 11) N(79, 11) N(91, 11) 2018
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 <- global_economy %>%
filter(Country =="China")
china %>% autoplot(GDP)
Because of the increase in the vairance, I will use BOX COX transformation to solve this problem.
lambda <- china %>%
features(GDP, features = guerrero) %>%
pull(lambda_guerrero)
bc_china <- china %>% autoplot(box_cox(GDP, lambda))
head(bc_china)
## $data
## # A tsibble: 58 x 9 [1Y]
## # Key: Country [1]
## Country Code Year GDP Growth CPI Imports Exports Population
## <fct> <fct> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 China CHN 1960 59716467625. NA NA 4.43 4.31 667070000
## 2 China CHN 1961 50056868958. -27.3 NA 3.49 3.87 660330000
## 3 China CHN 1962 47209359006. -5.58 NA 2.91 4.05 665770000
## 4 China CHN 1963 50706799903. 10.3 NA 2.86 4.01 682335000
## 5 China CHN 1964 59708343489. 18.2 NA 2.86 3.77 698355000
## 6 China CHN 1965 70436266147. 17.0 NA 3.19 3.64 715185000
## 7 China CHN 1966 76720285970. 10.7 NA 3.24 3.49 735400000
## 8 China CHN 1967 72881631327. -5.77 NA 2.98 3.28 754550000
## 9 China CHN 1968 70846535056. -4.10 NA 2.92 3.30 774510000
## 10 China CHN 1969 79705906247. 16.9 NA 2.41 3.05 796025000
## # … with 48 more rows
##
## $layers
## $layers[[1]]
## geom_line: na.rm = FALSE, orientation = NA
## stat_identity: na.rm = FALSE
## position_identity
##
##
## $scales
## <ggproto object: Class ScalesList, gg>
## add: function
## clone: function
## find: function
## get_scales: function
## has_scale: function
## input: function
## n: function
## non_position_scales: function
## scales: list
## super: <ggproto object: Class ScalesList, gg>
##
## $mapping
## Aesthetic mapping:
## * `x` -> `Year`
## * `y` -> `box_cox(GDP, lambda)`
##
## $theme
## list()
##
## $coordinates
## <ggproto object: Class CoordCartesian, Coord, gg>
## aspect: function
## backtransform_range: function
## clip: on
## default: TRUE
## distance: function
## expand: TRUE
## is_free: function
## is_linear: function
## labels: function
## limits: list
## modify_scales: function
## range: function
## render_axis_h: function
## render_axis_v: function
## render_bg: function
## render_fg: function
## setup_data: function
## setup_layout: function
## setup_panel_guides: function
## setup_panel_params: function
## setup_params: function
## train_panel_guides: function
## transform: function
## super: <ggproto object: Class CoordCartesian, Coord, gg>
Not it looks more of a trend model.
fit <- china %>%
model(est=ETS(GDP), #ETS model
ets_damped = ETS(GDP ~ trend("ad")), #damped model
ets_bc = ETS(box_cox(GDP, lambda)), # ets with box cox trasnformation
ets_bc_damped = ETS(box_cox(GDP, lambda) ~ trend ("Ad")) ,#damped with boxcox trasnforamtion
ets_log = ETS(log(GDP))
)
fit %>%
forecast(h="10 years") %>%
autoplot(china, level = NULL)
For the chart we can see the box-cox and log have great impact on the forecast, while damping has a small effect.
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?
aus_production %>% autoplot(Gas)
fit <- aus_production %>%
filter(Quarter > yearquarter("1990 Q4")) %>%
model(fit = ETS(Gas))
report(fit)
## Series: Gas
## Model: ETS(A,A,A)
## Smoothing parameters:
## alpha = 0.4902673
## beta = 0.0001000091
## gamma = 0.0001001453
##
## Initial states:
## l[0] b[0] s[0] s[-1] s[-2] s[-3]
## 133.4487 1.227524 -12.80569 26.37931 10.65398 -24.2276
##
## sigma^2: 28.4997
##
## AIC AICc BIC
## 610.6743 613.3213 631.8847
fit %>%
forecast(h = "3 years") %>%
autoplot(aus_production)
The multiplicative seasonality is necessary because the seasonal variation increases overtime.
Recall your retail time series data (from Exercise 8 in Section 2.10).
set.seed(123)
myseries <- aus_retail %>%
filter(`Series ID` == "A3349337W")
myseries %>% autoplot(Turnover)
Because for the chart we can see there is an increase over time, so multiplicative seasonality necessary is really important because of the increase is not consistant.
fit <- myseries %>% model( "Holt-Winters' Damped" = ETS(Turnover ~ error("M") + trend("Ad") + season("M")),
"Holt-Winters' Multiplicative" = ETS(Turnover ~ error("M") + trend("A") + season("M")))
fc <- fit %>% forecast(h = "10 years")
fc %>%
autoplot(myseries, level = NULL) + labs(title="Australian retail", y="Turnover") +
guides(colour = guide_legend(title = "Forecast"))
The damped methoed is less than the mutiplicative.
accuracy(fit) %>% select(".model", "RMSE")
## # A tibble: 2 × 2
## .model RMSE
## <chr> <dbl>
## 1 Holt-Winters' Damped 14.6
## 2 Holt-Winters' Multiplicative 15.0
When comparing the two methods the that Holt-Winters’ damped model has better RMSE than Holt-Winters’ multiplicative method. So Holt-Winter’s damped is better for this time series regarding the RMSE value.
fit %>% select("Holt-Winters' Damped") %>% gg_tsresiduals()
THE plot and histogram, we can say the residuals look like a white noise with occasional spikes, but when we check the ACF plot, it shows this method is not white noise, because more than 5% of the spikes is out of the bound. So we will try another method (multiplicative)
fit %>% select("Holt-Winters' Multiplicative") %>% gg_tsresiduals()
Multiplicative yield plots are better than damped. We can see on the ACF plot, spikes that out of the bound less than before.
# Split the data we use
myseries_train <- myseries %>%
filter(year(Month) < 2011)
# Add seasonal naïve model to our fit model
fit <- myseries_train %>% model(
"Holt-Winters' Damped" = ETS(Turnover ~ error(" M") + trend("Ad") + season("M")),
"Holt-Winters' Multiplicative" = ETS(Turnover ~ error("M") + trend("A") + season("M")),
"Seasonal Naïve Forecast" = SNAIVE(Turnover))
## Warning: 1 error encountered for Holt-Winters' Damped
## [1] Invalid error type
# Assign the data over 2011 to a variable as our forecast comparison later
Compare <- anti_join(myseries, myseries_train, by = c("State", "Industry", "Series ID", "Month", "Turnover"))
# Do the forecasting according to comparison data
fc <- fit %>% forecast(Compare)
# Call the Comparison plot
autoplot(Compare, Turnover) +
autolayer(fc, level = NULL) + guides(colour=guide_legend(title="Forecast")) + ggtitle('Comparison of Forecast')
## Warning: Removed 96 row(s) containing missing values (geom_path).
We see Holt-Winters’ Multiplicative method can beat the seasonal naïve approach. Otherwise Holt- Winters’Damped method didn’t show any significant difference with seasonal naïve approach.
For the same retail data, try an STL decomposition applied to the Box-Cox transformed series, followed by ETS on the seasonally adjusted data. How does that compare with your best previous forecasts on the test set?
lambda <- myseries_train %>%
features(Turnover, features = guerrero) %>%
pull(lambda_guerrero)
ts_bc <- myseries_train %>%
mutate(bc = box_cox(Turnover, lambda))
fit <- ts_bc %>%
model("STL (BoxCox)" = STL(bc~season(window = "periodic"),
robust = T),
'ETS (BoxCox)' = ETS(bc))
fit_best <- ts_bc %>%
model("Holt-Winters' Multiplicative" = ETS(Turnover ~ error("M")+ trend("A")+ season("M"))
)
rbind(accuracy(fit), accuracy(fit_best))
## # A tibble: 3 × 12
## State Industry .model .type ME RMSE MAE MPE MAPE MASE RMSSE ACF1
## <chr> <chr> <chr> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 New S… Hardwar… STL (… Trai… 0.320 12.9 8.67 -0.399 5.40 0.421 0.507 0.355
## 2 New S… Hardwar… ETS (… Trai… -1.74 13.2 9.37 -1.47 5.85 0.455 0.519 0.221
## 3 New S… Hardwar… Holt-… Trai… -1.14 13.5 9.55 -0.912 5.79 0.450 0.515 0.247
We can see the box cox trasnformation is better