DATA_624_Homework_3

Author

Long Lin

library(fpp3)
── Attaching packages ──────────────────────────────────────────── fpp3 1.0.3 ──
✔ tibble      3.3.1     ✔ tsibble     1.2.0
✔ dplyr       1.2.0     ✔ tsibbledata 0.4.1
✔ tidyr       1.3.2     ✔ ggtime      1.0.0
✔ lubridate   1.9.5     ✔ feasts      0.5.0
✔ ggplot2     4.0.2     ✔ fable       0.5.0
── 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()

Exercise 5.1

Produce forecasts for the following series using whichever of NAIVE(y), SNAIVE(y) or RW(y ~ drift()) is more appropriate in each case:

Australian Population (global_economy)

aus_pop <- global_economy |>
  filter(Country == "Australia") |>
  filter(!is.na(Population)) |>
  select(Population)
aus_pop
# A tsibble: 58 x 2 [1Y]
   Population  Year
        <dbl> <dbl>
 1   10276477  1960
 2   10483000  1961
 3   10742000  1962
 4   10950000  1963
 5   11167000  1964
 6   11388000  1965
 7   11651000  1966
 8   11799000  1967
 9   12009000  1968
10   12263000  1969
# ℹ 48 more rows
aus_pop |>
  autoplot(Population)

pop_fit <- aus_pop |>
  model(Drift = RW(Population ~ drift()))
pop_fit
# A mable: 1 x 1
          Drift
        <model>
1 <RW w/ drift>
pop_fc <- pop_fit |>
  forecast(h = "5 years")
pop_fc
# A fable: 5 x 4 [1Y]
# Key:     .model [1]
  .model  Year          Population
  <chr>  <dbl>              <dist>
1 Drift   2018 N(2.5e+07, 6.1e+09)
2 Drift   2019 N(2.5e+07, 1.2e+10)
3 Drift   2020 N(2.5e+07, 1.9e+10)
4 Drift   2021 N(2.6e+07, 2.6e+10)
5 Drift   2022 N(2.6e+07, 3.3e+10)
# ℹ 1 more variable: .mean <dbl>
pop_fc |>
  autoplot(aus_pop, level = NULL) +
  labs(title = "Australian Population", y = "Total Population")

For this data set, I choose the Drift Forecast Method because the visualization showed a linear increase. If I used the Naive or Seasonal Naive method, it would’ve given an incorrect forecast based off of the linear trend.

Bricks (aus_production)

bricks <- aus_production |>
  filter(!is.na(Bricks)) |>
  select(Bricks)
bricks
# A tsibble: 198 x 2 [1Q]
   Bricks Quarter
    <dbl>   <qtr>
 1    189 1956 Q1
 2    204 1956 Q2
 3    208 1956 Q3
 4    197 1956 Q4
 5    187 1957 Q1
 6    214 1957 Q2
 7    227 1957 Q3
 8    222 1957 Q4
 9    199 1958 Q1
10    229 1958 Q2
# ℹ 188 more rows
bricks |>
  autoplot(Bricks)

bricks_fit <- bricks |>
  model(Seasonal_naive = SNAIVE(Bricks))
bricks_fit
# A mable: 1 x 1
  Seasonal_naive
         <model>
1       <SNAIVE>
bricks_fc <- bricks_fit |>
  forecast(h = "5 years")
bricks_fc
# A fable: 20 x 4 [1Q]
# Key:     .model [1]
   .model         Quarter
   <chr>            <qtr>
 1 Seasonal_naive 2005 Q3
 2 Seasonal_naive 2005 Q4
 3 Seasonal_naive 2006 Q1
 4 Seasonal_naive 2006 Q2
 5 Seasonal_naive 2006 Q3
 6 Seasonal_naive 2006 Q4
 7 Seasonal_naive 2007 Q1
 8 Seasonal_naive 2007 Q2
 9 Seasonal_naive 2007 Q3
10 Seasonal_naive 2007 Q4
11 Seasonal_naive 2008 Q1
12 Seasonal_naive 2008 Q2
13 Seasonal_naive 2008 Q3
14 Seasonal_naive 2008 Q4
15 Seasonal_naive 2009 Q1
16 Seasonal_naive 2009 Q2
17 Seasonal_naive 2009 Q3
18 Seasonal_naive 2009 Q4
19 Seasonal_naive 2010 Q1
20 Seasonal_naive 2010 Q2
# ℹ 2 more variables: Bricks <dist>, .mean <dbl>
bricks_fc |>
  autoplot(bricks, level = NULL) +
  labs(title = "Clay brick production in Australia", y = "Millions of bricks")

For this data set, I choose to use the Seasonal Naive (SNAIVE) Forecast Method because the data set is very seasonal. A naive forecast wouldn’t have been taken into account the seasonality. A drift forecast would’ve also ignored the obvious seasonal variations.

NSW Lambs (aus_livestock)

NSW_Lambs <- aus_livestock |>
  filter(!is.na(Count), State == "New South Wales", Animal == "Lambs") |>
  select(Count)
NSW_Lambs
# A tsibble: 558 x 2 [1M]
    Count    Month
    <dbl>    <mth>
 1 587600 1972 Jul
 2 553700 1972 Aug
 3 494900 1972 Sep
 4 533500 1972 Oct
 5 574300 1972 Nov
 6 517500 1972 Dec
 7 562600 1973 Jan
 8 426900 1973 Feb
 9 496300 1973 Mar
10 496000 1973 Apr
# ℹ 548 more rows
NSW_Lambs |>
  autoplot(Count)

lambs_fit <- NSW_Lambs |>
  model(Seasonal_naive = SNAIVE(Count))
lambs_fit
# A mable: 1 x 1
  Seasonal_naive
         <model>
1       <SNAIVE>
lambs_fc <- lambs_fit |>
  forecast(h = "5 years")
lambs_fc
# A fable: 60 x 4 [1M]
# Key:     .model [1]
   .model            Month
   <chr>             <mth>
 1 Seasonal_naive 2019 Jan
 2 Seasonal_naive 2019 Feb
 3 Seasonal_naive 2019 Mar
 4 Seasonal_naive 2019 Apr
 5 Seasonal_naive 2019 May
 6 Seasonal_naive 2019 Jun
 7 Seasonal_naive 2019 Jul
 8 Seasonal_naive 2019 Aug
 9 Seasonal_naive 2019 Sep
10 Seasonal_naive 2019 Oct
# ℹ 50 more rows
# ℹ 2 more variables: Count <dist>, .mean <dbl>
lambs_fc |>
  autoplot(NSW_Lambs, level = NULL) +
  labs(title = "New South Wales Lamb Production", y = "NUmber of lambs slaughtered")

This data set, like the previous data set, also exhibits seasonal changes. Therefore, I used the SNAIVE Forecast Method for this forecast as well.

Household wealth (hh_budget)

hh_wealth <- hh_budget |>
  filter(!is.na(Wealth)) |>
  select(Wealth)
hh_wealth
# A tsibble: 88 x 3 [1Y]
# Key:       Country [4]
   Wealth  Year Country  
    <dbl> <dbl> <chr>    
 1   315.  1995 Australia
 2   315.  1996 Australia
 3   323.  1997 Australia
 4   339.  1998 Australia
 5   354.  1999 Australia
 6   350.  2000 Australia
 7   348.  2001 Australia
 8   349.  2002 Australia
 9   360.  2003 Australia
10   379.  2004 Australia
# ℹ 78 more rows
hh_wealth |>
  autoplot(Wealth)

hh_wealth_fit <- hh_wealth |>
  model(Drift = RW(Wealth ~ drift()))
hh_wealth_fit
# A mable: 4 x 2
# Key:     Country [4]
  Country           Drift
  <chr>           <model>
1 Australia <RW w/ drift>
2 Canada    <RW w/ drift>
3 Japan     <RW w/ drift>
4 USA       <RW w/ drift>
hh_wealth_fc <- hh_wealth_fit |>
  forecast(h = "5 years")
hh_wealth_fc
# A fable: 20 x 5 [1Y]
# Key:     Country, .model [4]
   Country   .model  Year
   <chr>     <chr>  <dbl>
 1 Australia Drift   2017
 2 Australia Drift   2018
 3 Australia Drift   2019
 4 Australia Drift   2020
 5 Australia Drift   2021
 6 Canada    Drift   2017
 7 Canada    Drift   2018
 8 Canada    Drift   2019
 9 Canada    Drift   2020
10 Canada    Drift   2021
11 Japan     Drift   2017
12 Japan     Drift   2018
13 Japan     Drift   2019
14 Japan     Drift   2020
15 Japan     Drift   2021
16 USA       Drift   2017
17 USA       Drift   2018
18 USA       Drift   2019
19 USA       Drift   2020
20 USA       Drift   2021
# ℹ 2 more variables: Wealth <dist>, .mean <dbl>
hh_wealth_fc |>
  autoplot(hh_wealth, level = NULL) +
  labs(title = "Household Wealth", y = "Wealth as percentage of net disposable income")

For this data set, I chose to use the Drift Forecast Method since it doesn’t have seasonal changes. I chose not to use a NAIVE Forecast because there is a upward trend in the data and a NAIVE Forecast would’ve just projected the most recent observation as the forecast.

Australian takeaway food turnover (aus_retail)

aus_takeaway_food_turnover <- aus_retail |>
  filter(!is.na(Turnover), Industry == "Cafes, restaurants and takeaway food services") |>
  select(Turnover)
aus_takeaway_food_turnover
# A tsibble: 3,456 x 4 [1M]
# Key:       State, Industry [8]
   Turnover    Month State                        Industry                      
      <dbl>    <mth> <chr>                        <chr>                         
 1      7.6 1982 Apr Australian Capital Territory Cafes, restaurants and takeaw…
 2      6.7 1982 May Australian Capital Territory Cafes, restaurants and takeaw…
 3      7.1 1982 Jun Australian Capital Territory Cafes, restaurants and takeaw…
 4      7.5 1982 Jul Australian Capital Territory Cafes, restaurants and takeaw…
 5      7.3 1982 Aug Australian Capital Territory Cafes, restaurants and takeaw…
 6      8.1 1982 Sep Australian Capital Territory Cafes, restaurants and takeaw…
 7      8.9 1982 Oct Australian Capital Territory Cafes, restaurants and takeaw…
 8      9.6 1982 Nov Australian Capital Territory Cafes, restaurants and takeaw…
 9     11.2 1982 Dec Australian Capital Territory Cafes, restaurants and takeaw…
10      7.7 1983 Jan Australian Capital Territory Cafes, restaurants and takeaw…
# ℹ 3,446 more rows
aus_takeaway_food_turnover |>
  autoplot(Turnover)

takeaway_fit <- aus_takeaway_food_turnover |>
  model(Drift = RW(Turnover ~ drift()))
takeaway_fit
# A mable: 8 x 3
# Key:     State, Industry [8]
  State                        Industry                                    Drift
  <chr>                        <chr>                                     <model>
1 Australian Capital Territory Cafes, restaurants and takeaway fo… <RW w/ drift>
2 New South Wales              Cafes, restaurants and takeaway fo… <RW w/ drift>
3 Northern Territory           Cafes, restaurants and takeaway fo… <RW w/ drift>
4 Queensland                   Cafes, restaurants and takeaway fo… <RW w/ drift>
5 South Australia              Cafes, restaurants and takeaway fo… <RW w/ drift>
6 Tasmania                     Cafes, restaurants and takeaway fo… <RW w/ drift>
7 Victoria                     Cafes, restaurants and takeaway fo… <RW w/ drift>
8 Western Australia            Cafes, restaurants and takeaway fo… <RW w/ drift>
takeaway_fc <- takeaway_fit |>
  forecast(h = "5 years")
takeaway_fc
# A fable: 480 x 6 [1M]
# Key:     State, Industry, .model [8]
   State                        Industry                         .model    Month
   <chr>                        <chr>                            <chr>     <mth>
 1 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Jan
 2 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Feb
 3 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Mar
 4 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Apr
 5 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 May
 6 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Jun
 7 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Jul
 8 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Aug
 9 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Sep
10 Australian Capital Territory Cafes, restaurants and takeaway… Drift  2019 Oct
# ℹ 470 more rows
# ℹ 2 more variables: Turnover <dist>, .mean <dbl>
takeaway_fc |>
  autoplot(aus_takeaway_food_turnover, level = NULL) +
  labs(title = "Australian Takeaway Food Turnover", y = "Turnover in $Million AUD") +
  facet_wrap(vars(State), ncol = 2, scales = "free_y")

For this data set, I chose to use the Drift Forecast Method because the plot has an upward trend which wouldn’t have been displayed correctly with either of the SNAIVE or NAIVE Forecast methods. There are seasonal changes so I think a method that incorporates both a Drift Forecast and SNAIVE Forecast would’ve worked the best.

Exercise 5.2

Use the Facebook stock price (data set gafa_stock) to do the following:

Produce a time plot of the series.

fb_stock_price <- gafa_stock |>
  filter(!is.na(Close), Symbol == "FB") |>
  mutate(trading_day = row_number()) |>
  update_tsibble(index = trading_day, regular = TRUE) |>
  select(Close)
fb_stock_price
# A tsibble: 1,258 x 2 [1]
   Close trading_day
   <dbl>       <int>
 1  54.7           1
 2  54.6           2
 3  57.2           3
 4  57.9           4
 5  58.2           5
 6  57.2           6
 7  57.9           7
 8  55.9           8
 9  57.7           9
10  57.6          10
# ℹ 1,248 more rows
fb_stock_price |>
  autoplot(Close) +
  labs(title = "Facebook Closing Stock Price", y = "Price $USD")

Produce forecasts using the drift method and plot them.

fb_stock_fit<- fb_stock_price |>
  model(Drift = RW(Close ~ drift()))
fb_stock_fit
# A mable: 1 x 1
          Drift
        <model>
1 <RW w/ drift>
fb_stock_fc <- fb_stock_fit |>
  forecast(h = 100)
fb_stock_fc
# A fable: 100 x 4 [1]
# Key:     .model [1]
   .model trading_day
   <chr>        <dbl>
 1 Drift         1259
 2 Drift         1260
 3 Drift         1261
 4 Drift         1262
 5 Drift         1263
 6 Drift         1264
 7 Drift         1265
 8 Drift         1266
 9 Drift         1267
10 Drift         1268
# ℹ 90 more rows
# ℹ 2 more variables: Close <dist>, .mean <dbl>
fb_stock_fc |>
  autoplot(fb_stock_price, level = NULL) +
  labs(title = "Facebook Closing Stock Price", y = "Price $USD")

Show that the forecasts are identical to extending the line drawn between the first and last observations.

first_obs <- fb_stock_price |> slice(1)
last_obs <- fb_stock_price |> slice(n())

line_slope <- (last_obs$Close - first_obs$Close) / 
              (last_obs$trading_day - first_obs$trading_day)
              
line_intercept <- first_obs$Close - (line_slope * first_obs$trading_day)

fb_stock_fc |>
  autoplot(fb_stock_price, level = NULL) +
  geom_abline(intercept = line_intercept, slope = line_slope, 
              color = "red", linetype = "dashed") +
  labs(title = "Facebook Closing Stock Price", y = "Price $USD")

The dashed red line shows the line between the first and last observation. The forecast follows directly on that line.

Try using some of the other benchmark functions to forecast the same data set. Which do you think is best? Why?

fb_fit <- fb_stock_price |>
  model(
    Mean = MEAN(Close),
    Naive = NAIVE(Close),
    Drift = RW(Close ~ drift())
  )
fb_fit
# A mable: 1 x 3
     Mean   Naive         Drift
  <model> <model>       <model>
1  <MEAN> <NAIVE> <RW w/ drift>
fb_fc <- fb_fit |>
  forecast(h = 100)
fb_fc
# A fable: 300 x 4 [1]
# Key:     .model [3]
   .model trading_day
   <chr>        <dbl>
 1 Mean          1259
 2 Mean          1260
 3 Mean          1261
 4 Mean          1262
 5 Mean          1263
 6 Mean          1264
 7 Mean          1265
 8 Mean          1266
 9 Mean          1267
10 Mean          1268
# ℹ 290 more rows
# ℹ 2 more variables: Close <dist>, .mean <dbl>
fb_fc |>
  autoplot(fb_stock_price, level = NULL) +
  labs(
    title = "Facebook Closing Stock Price Benchmark Forecasts", y = "Price $USD"
  )

I believe that the Naive function is the best benchmark function because this is financial data. In an efficient market, the best predictor of price is the current price.

Exercise 5.3

Apply a seasonal naïve method to the quarterly Australian beer production data from 1992. Check if the residuals look like white noise, and plot the forecasts. The following code will help. What do you conclude?

# Extract data of interest
recent_production <- aus_production |>
  filter(year(Quarter) >= 1992)
# Define and estimate a model
fit <- recent_production |> model(SNAIVE(Beer))
# Look at the residuals
fit |> gg_tsresiduals()
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 4 rows containing non-finite outside the scale range
(`stat_bin()`).
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_rug()`).

The residuals do not look like white noise based on the acf plot even though the time plot shows homoscedasticity and the histogram seems to have a zero mean. At lag 4, there is a massive spike that goes past the blue dashed line. This shows that there is a repeating annual pattern that is not captured in the model since the data is quarterly and 4 quarters make up a year.

# Look at some forecasts
fit |> forecast() |> autoplot(recent_production)

augment(fit) |> 
  features(.innov, ljung_box, lag = 8)
# A tibble: 1 × 3
  .model       lb_stat lb_pvalue
  <chr>          <dbl>     <dbl>
1 SNAIVE(Beer)    32.3 0.0000834

Using the Ljung-box test with a lag of 8 or 2 year window, the p value of 8.335611e-05 also shows that the residuals are not white noise.

Exercise 5.4

Repeat the previous exercise using the Australian Exports series from global_economy and the Bricks series from aus_production. Use whichever of NAIVE() or SNAIVE() is more appropriate in each case.

Australian Exports series from global_economy

# Extract data of interest
aus_exports <- global_economy |>
  filter(!is.na(Exports), Country == "Australia") |>
  select(Exports)
aus_exports
# A tsibble: 58 x 2 [1Y]
   Exports  Year
     <dbl> <dbl>
 1    13.0  1960
 2    12.4  1961
 3    13.9  1962
 4    13.0  1963
 5    14.9  1964
 6    13.2  1965
 7    12.9  1966
 8    12.9  1967
 9    12.3  1968
10    12.0  1969
# ℹ 48 more rows
# Define and estimate a model
aus_exports_fit <- aus_exports |> model(NAIVE(Exports))
# Look at the residuals
aus_exports_fit |> gg_tsresiduals()
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 1 row containing non-finite outside the scale range
(`stat_bin()`).
Warning: Removed 1 row containing missing values or values outside the scale range
(`geom_rug()`).

NAIVE() is more appropriate for this data because it’s annual data and therefore doesn’t have seasonality for a SNAIVE(). The histogram shows a zero mean and the time plot shows general homoscedasticity. For this data set, there is a similar issue at lag 1 for the ACF plot that goes past the blue dashed line. In order to do a further assessment, I’ll use the Ljung-box test.

# Look at some forecasts
aus_exports_fit |> forecast() |> autoplot(aus_exports)

augment(aus_exports_fit) |> 
  features(.innov, ljung_box, lag = 10)
# A tibble: 1 × 3
  .model         lb_stat lb_pvalue
  <chr>            <dbl>     <dbl>
1 NAIVE(Exports)    16.4    0.0896

Using the Ljung-box test with a lag of 10, it returns a p-value of 0.08963678. This shows that the residuals are not distinguishable from a white noise series considering the p-value is greater than 0.05.

Bricks series from aus_production

bricks <- aus_production |>
  filter(!is.na(Bricks)) |>
  select(Bricks)
bricks
# A tsibble: 198 x 2 [1Q]
   Bricks Quarter
    <dbl>   <qtr>
 1    189 1956 Q1
 2    204 1956 Q2
 3    208 1956 Q3
 4    197 1956 Q4
 5    187 1957 Q1
 6    214 1957 Q2
 7    227 1957 Q3
 8    222 1957 Q4
 9    199 1958 Q1
10    229 1958 Q2
# ℹ 188 more rows
# Define and estimate a model
bricks_fit <- bricks |> model(SNAIVE(Bricks))
# Look at the residuals
bricks_fit |> gg_tsresiduals()
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 4 rows containing non-finite outside the scale range
(`stat_bin()`).
Warning: Removed 4 rows containing missing values or values outside the scale range
(`geom_rug()`).

I chose to use a SNAIVE() model for this data set because it is quarterly data and has seasonal changes. These residuals do not look like white noise considering the acf plot has multiple points that cross the blue dashed line. The time plot also does not display homoscedasticity with the variance increasing drastically after 1970. The histogram also looks a bit left skewed as well.

# Look at some forecasts
bricks_fit |> forecast() |> autoplot(bricks)

augment(bricks_fit) |> 
  features(.innov, ljung_box, lag = 8)
# A tibble: 1 × 3
  .model         lb_stat lb_pvalue
  <chr>            <dbl>     <dbl>
1 SNAIVE(Bricks)    274.         0

Using the Ljung-box test with a lag of 8 (2 years of quarterly data) returns a 0 p-value. This supports that the residuals are not white noise.

Exercise 5.7

For your retail time series (from Exercise 7 in Section 2.10):

a. Create a training dataset consisting of observations before 2011 using

set.seed(8585)
myseries <- aus_retail |>
  filter(`Series ID` == sample(aus_retail$`Series ID`,1))

myseries_train <- myseries |>
  filter(year(Month) < 2011)
myseries_train
# A tsibble: 345 x 5 [1M]
# Key:       State, Industry [1]
   State           Industry               `Series ID`    Month Turnover
   <chr>           <chr>                  <chr>          <mth>    <dbl>
 1 New South Wales Other retailing n.e.c. A3349873A   1982 Apr     62.4
 2 New South Wales Other retailing n.e.c. A3349873A   1982 May     63.1
 3 New South Wales Other retailing n.e.c. A3349873A   1982 Jun     59.6
 4 New South Wales Other retailing n.e.c. A3349873A   1982 Jul     61.9
 5 New South Wales Other retailing n.e.c. A3349873A   1982 Aug     60.7
 6 New South Wales Other retailing n.e.c. A3349873A   1982 Sep     61.2
 7 New South Wales Other retailing n.e.c. A3349873A   1982 Oct     62.1
 8 New South Wales Other retailing n.e.c. A3349873A   1982 Nov     68.3
 9 New South Wales Other retailing n.e.c. A3349873A   1982 Dec    104  
10 New South Wales Other retailing n.e.c. A3349873A   1983 Jan     63.9
# ℹ 335 more rows

b. Check that your data have been split appropriately by producing the following plot.

autoplot(myseries, Turnover) +
  autolayer(myseries_train, Turnover, colour = "red")

The data has been split appropriately using data before 2011.

c. Fit a seasonal naïve model using SNAIVE() applied to your training data (myseries_train).

my_series_fit <- myseries_train |>
  model(SNAIVE(Turnover))
my_series_fit
# A mable: 1 x 3
# Key:     State, Industry [1]
  State           Industry               `SNAIVE(Turnover)`
  <chr>           <chr>                             <model>
1 New South Wales Other retailing n.e.c.           <SNAIVE>

d. Check the residuals. Do the residuals appear to be uncorrelated and normally distributed?

my_series_fit |> gg_tsresiduals()
Warning: Removed 12 rows containing missing values or values outside the scale range
(`geom_line()`).
Warning: Removed 12 rows containing missing values or values outside the scale range
(`geom_point()`).
Warning: Removed 12 rows containing non-finite outside the scale range
(`stat_bin()`).
Warning: Removed 12 rows containing missing values or values outside the scale range
(`geom_rug()`).

The residuals appear to be normally distributed but are not uncorrelated. The acf plot shows many positive spikes past the blue dashed line from lag 1 to lag 9. There is also negative spikes at lag 12 and lag 25. The time plot also does not exhibit homoscedasticity with the variance being irregular. This shows that the residuals are not white noise.

e. Produce forecasts for the test data

my_series_fc <- my_series_fit |>
  forecast(new_data = anti_join(myseries, myseries_train))
Joining with `by = join_by(State, Industry, `Series ID`, Month, Turnover)`
my_series_fc |> autoplot(myseries)

f. Compare the accuracy of your forecasts against the actual values.

my_series_fit |> accuracy()
# A tibble: 1 × 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 Sou… Other r… SNAIV… Trai…  7.77  20.2  16.0  4.70  8.11     1     1 0.739
my_series_fc |> accuracy(myseries)
# A tibble: 1 × 12
  .model    State Industry .type    ME  RMSE   MAE   MPE  MAPE  MASE RMSSE  ACF1
  <chr>     <chr> <chr>    <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 SNAIVE(T… New … Other r… Test   190.  215.  190.  37.1  37.1  11.9  10.6 0.871

The forecasts were not accurate when compared against the actual values. The actual values were significantly higher since there was an upward trend from 2011 onwards. Meanwhile since the forecast used a SNAIVE Forecast Method, it only used the previous seasonal values to predict the forecasts. The values were off by over 30% between the forecast and actual values according to the MAPE value of 37.13291.

g. How sensitive are the accuracy measures to the amount of training data used?

The accuracy measures are highly sensitive to the amount of training data used. Too little data and the model might not incorporate long trends. The SNAIVE Forecast did not take into account the previous upward trend before the year 2000 because it only grabbed the last seasonal data.

Too much data might result in the model incorporating one off events that don’t happen regularly. If something like a economic depression happened in the last season then the forecast would also be wildly inaccurate considering economic depressions are usually not seasonal and are more irregular.