Load packages and data

library(fpp3)
library(readxl)
library(cowplot)

My Dataset

Milky_boy <- read_excel("~/Downloads/Milky boy.xlsx", 
                        col_types = c("date", "numeric", "text"))

Cleaning and OG Plot

milk <- Milky_boy %>%
  mutate(Month = yearmonth(Year)) %>%
  select(Month, Price) %>%
  as_tsibble(index = Month)

mplot <- autoplot(milk) +
  labs(y = "Price in $", x = "Months", title = "Monthly Gallon Of Milk Prices")
## Plot variable not specified, automatically selected `.vars = Price`
mplot

STL Decomp

milk %>%
  model(STL(Price ~ season(window = 13), robust = TRUE)) %>%
  components() %>%
  autoplot() +
  labs(title = "STL Decomp")

Data not stationary and needs 1 diff

milk %>%
  features(Price, unitroot_kpss)
## # A tibble: 1 × 2
##   kpss_stat kpss_pvalue
##       <dbl>       <dbl>
## 1      1.29        0.01
milk %>%
  features(Price, unitroot_ndiffs)
## # A tibble: 1 × 1
##   ndiffs
##    <int>
## 1      1
milk %>%
  gg_tsdisplay(Price, plot_type = 'partial')

Diff check and partial resid of diff

Milkdiff2 <- milk %>%
  mutate(Price = difference(Price)) %>%
  na.omit()

Milkdiff2 %>%
  ggplot(aes(x = Month, y = Price)) +
  geom_line(aes(y = Price)) 

Milkdiff2 %>%
  features(Price, unitroot_kpss)
## # A tibble: 1 × 2
##   kpss_stat kpss_pvalue
##       <dbl>       <dbl>
## 1    0.0914         0.1
Milkdiff2 %>%
  features(Price, unitroot_ndiffs)
## # A tibble: 1 × 1
##   ndiffs
##    <int>
## 1      0
Milkdiff2 %>%
  gg_tsdisplay(Price, plot_type = 'partial')

ARIMA auto

Milkauto <- milk %>%
  model('Auto' = ARIMA(Price))

report(Milkauto)
## Series: Price 
## Model: ARIMA(0,1,2) 
## 
## Coefficients:
##          ma1     ma2
##       0.3441  0.1289
## s.e.  0.0613  0.0714
## 
## sigma^2 estimated as 0.004806:  log likelihood=342.2
## AIC=-678.41   AICc=-678.32   BIC=-667.58

ARIMA Compare

Milkcomp <- milk %>% 
  model(
    '012' = ARIMA(Price ~ pdq(0,1,2)),
    '212' = ARIMA(Price ~ pdq(2,1,2)),
    '110' = ARIMA(Price ~ pdq(1,1,0)),
    "011" = ARIMA(Price ~ pdq(0,1,1)),
    "111" = ARIMA(Price ~ pdq(1,1,1))) 

Milkcomp %>%
  glance()
## # A tibble: 5 × 8
##   .model  sigma2 log_lik   AIC  AICc   BIC ar_roots  ma_roots 
##   <chr>    <dbl>   <dbl> <dbl> <dbl> <dbl> <list>    <list>   
## 1 012    0.00481    342. -678. -678. -668. <cpl [0]> <cpl [2]>
## 2 212    0.00474    345. -680. -680. -662. <cpl [2]> <cpl [2]>
## 3 110    0.00483    341. -678. -678. -671. <cpl [1]> <cpl [0]>
## 4 011    0.00485    341. -677. -677. -670. <cpl [0]> <cpl [1]>
## 5 111    0.00483    341. -677. -677. -666. <cpl [1]> <cpl [1]>
#012 and 212

ETS Auto

MilkETSauto <- milk %>%
  model('Auto' = ETS(Price))

report(MilkETSauto)
## Series: Price 
## Model: ETS(M,N,N) 
##   Smoothing parameters:
##     alpha = 0.9999 
## 
##   Initial states:
##      l[0]
##  2.783582
## 
##   sigma^2:  5e-04
## 
##      AIC     AICc      BIC 
## 107.6908 107.7796 118.5301

ETS Comp

MilkETS <- milk %>% 
  model(
    'Auto' = ETS(Price),
    'Simple' = ETS(Price ~ error("A") + trend("N") + season("N")),
    'Holt' = ETS(Price ~ error("A") + trend("A") + season("N")),
    'Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N"))) 

MilkETS %>%
  glance()
## # A tibble: 4 × 9
##   .model                 sigma2 log_lik   AIC  AICc   BIC     MSE   AMSE    MAE
##   <chr>                   <dbl>   <dbl> <dbl> <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1 Auto                 0.000508   -50.8  108.  108.  119. 0.00532 0.0143 0.0150
## 2 Simple               0.00536    -51.7  109.  110.  120. 0.00532 0.0143 0.0493
## 3 Holt                 0.00542    -52.3  115.  115.  133. 0.00535 0.0142 0.0494
## 4 Multiplicative Trend 0.00539    -51.4  113.  113.  131. 0.00531 0.0142 0.0493
#Auto

Test and Training sets

choochoo <- milk %>%
  filter(year(Month) <= 2015)
choochoo
## # A tsibble: 192 x 2 [1M]
##       Month Price
##       <mth> <dbl>
##  1 2000 Jan  2.78
##  2 2000 Feb  2.78
##  3 2000 Mar  2.75
##  4 2000 Apr  2.77
##  5 2000 May  2.78
##  6 2000 Jun  2.76
##  7 2000 Jul  2.78
##  8 2000 Aug  2.81
##  9 2000 Sep  2.81
## 10 2000 Oct  2.80
## # … with 182 more rows
testies <- milk %>%
  filter(year(Month) > 2015)
testies
## # A tsibble: 82 x 2 [1M]
##       Month Price
##       <mth> <dbl>
##  1 2016 Jan  3.31
##  2 2016 Feb  3.23
##  3 2016 Mar  3.19
##  4 2016 Apr  3.16
##  5 2016 May  3.16
##  6 2016 Jun  3.12
##  7 2016 Jul  3.06
##  8 2016 Aug  3.14
##  9 2016 Sep  3.23
## 10 2016 Oct  3.29
## # … with 72 more rows

RMSE of first 4 models

Milkfour <- choochoo %>%
  model(
    'Mean' = MEAN(Price),
    'Naive' = NAIVE(Price),
    'Seasonal_naive' = SNAIVE(Price),
    'Drift' = RW(Price ~ drift()))

Milkacc <- Milkfour %>%
  forecast(h = 82)

accuracy(Milkacc, milk)
## # A tibble: 4 × 10
##   .model         .type        ME  RMSE   MAE    MPE  MAPE  MASE RMSSE  ACF1
##   <chr>          <chr>     <dbl> <dbl> <dbl>  <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Drift          Test  -0.115    0.334 0.271 -4.44   8.32 1.03  0.953 0.945
## 2 Mean           Test   0.0647   0.361 0.258  0.916  7.46 0.979 1.03  0.949
## 3 Naive          Test  -0.000817 0.355 0.270 -1.08   7.97 1.02  1.01  0.949
## 4 Seasonal_naive Test  -0.112    0.393 0.326 -4.50   9.89 1.24  1.12  0.867
#Drift lowest RMSE
#Mean has lowest MAPE

RMSE for ARIMA

Milktrain <- choochoo %>% 
  model(
    '012' = ARIMA(Price ~ pdq(0,1,2)),
    '212' = ARIMA(Price ~ pdq(2,1,2)),
    '110' = ARIMA(Price ~ pdq(1,1,0)),
    "011" = ARIMA(Price ~ pdq(0,1,1)),
    "111" = ARIMA(Price ~ pdq(1,1,1))) 

Milktrain %>%
  glance()
## # A tibble: 5 × 8
##   .model  sigma2 log_lik   AIC  AICc   BIC ar_roots  ma_roots 
##   <chr>    <dbl>   <dbl> <dbl> <dbl> <dbl> <list>    <list>   
## 1 012    0.00531    230. -454. -454. -445. <cpl [0]> <cpl [2]>
## 2 212    0.00515    234. -458. -457. -442. <cpl [2]> <cpl [2]>
## 3 110    0.00539    228. -452. -452. -446. <cpl [1]> <cpl [0]>
## 4 011    0.00545    227. -450. -450. -444. <cpl [0]> <cpl [1]>
## 5 111    0.00541    228. -451. -451. -441. <cpl [1]> <cpl [1]>
#012 and 212

best <- choochoo %>% 
  model(
    '012' = ARIMA(Price ~ pdq(0,1,2)),
    '212' = ARIMA(Price ~ pdq(2,1,2)),
    '110' = ARIMA(Price ~ pdq(1,1,0)),
    "011" = ARIMA(Price ~ pdq(0,1,1)),
    "111" = ARIMA(Price ~ pdq(1,1,1))) %>%
  forecast(h = 82)


accuracy(best, milk)
## # A tibble: 5 × 10
##   .model .type       ME  RMSE   MAE   MPE  MAPE  MASE RMSSE  ACF1
##   <chr>  <chr>    <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 011    Test  -0.00622 0.355 0.272 -1.25  8.02  1.03  1.01 0.949
## 2 012    Test  -0.0143  0.355 0.274 -1.50  8.12  1.04  1.01 0.949
## 3 110    Test  -0.00665 0.355 0.272 -1.26  8.03  1.03  1.01 0.949
## 4 111    Test  -0.00789 0.355 0.272 -1.30  8.04  1.03  1.01 0.949
## 5 212    Test  -0.00396 0.355 0.271 -1.18  8.00  1.03  1.01 0.949
#212 and 011 have lowest RMSE
#All RMSE are extremely close
#212 and 011 have lowest MAPE
#All are extremely close except 012

RMSE for ETS

Milkband <- choochoo %>% 
  model(
    'Auto' = ETS(Price),
    'Simple' = ETS(Price ~ error("A") + trend("N") + season("N")),
    'Holt' = ETS(Price ~ error("A") + trend("A") + season("N")),
    'Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N"))) 

Milkband %>%
  glance()
## # A tibble: 4 × 9
##   .model                 sigma2 log_lik   AIC  AICc   BIC     MSE   AMSE    MAE
##   <chr>                   <dbl>   <dbl> <dbl> <dbl> <dbl>   <dbl>  <dbl>  <dbl>
## 1 Auto                 0.000573   -10.3  32.6  33.0  52.1 0.00582 0.0177 0.0156
## 2 Simple               0.00615    -14.9  35.8  35.9  45.6 0.00608 0.0168 0.0506
## 3 Holt                 0.00648    -19.0  48.0  48.3  64.3 0.00635 0.0204 0.0537
## 4 Multiplicative Trend 0.00628    -15.9  41.8  42.1  58.1 0.00615 0.0168 0.0511
#Auto

MilkETS2 <- choochoo %>% 
  model(
    'Auto' = ETS(Price),
    'Simple' = ETS(Price ~ error("A") + trend("N") + season("N")),
    'Holt' = ETS(Price ~ error("A") + trend("A") + season("N")),
    'Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N"))) %>%
  forecast(h = 82)


accuracy(MilkETS2, milk)
## # A tibble: 4 × 10
##   .model              .type       ME  RMSE   MAE     MPE  MAPE  MASE RMSSE  ACF1
##   <chr>               <chr>    <dbl> <dbl> <dbl>   <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 Auto                Test   3.30e-2 0.357 0.263 -0.0530  7.66 0.996 1.02  0.949
## 2 Holt                Test   8.20e-1 1.12  0.831 23.0    23.4  3.15  3.19  0.960
## 3 Multiplicative Tre… Test  -6.83e-2 0.337 0.268 -3.07    8.09 1.01  0.962 0.947
## 4 Simple              Test  -8.16e-4 0.355 0.270 -1.08    7.97 1.02  1.01  0.949
#Multi has lowest RMSE

#Auto has lowest MAPE

Plots for all the best models

Milkdrift <- choochoo %>%
  model('Drift' = RW(Price ~ drift())) %>%
  forecast(h = 82)

plot1 <- Milkdrift %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "Drift")
## Plot variable not specified, automatically selected `.vars = Price`
bestfr <- choochoo %>% 
  model('012' = ARIMA(Price ~ pdq(0,1,2))) %>%
  forecast(h = 82)

plot2 <- bestfr %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (0,1,2)")
## Plot variable not specified, automatically selected `.vars = Price`
bestfr3 <- choochoo %>% 
  model('212' = ARIMA(Price ~ pdq(2,1,2))) %>%
  forecast(h = 82)

plot3 <- bestfr3 %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (2,1,2)")
## Plot variable not specified, automatically selected `.vars = Price`
bestfr4 <- choochoo %>% 
  model('011' = ARIMA(Price ~ pdq(0,1,1))) %>%
  forecast(h = 82)

plot4 <- bestfr4 %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (0,1,1)")
## Plot variable not specified, automatically selected `.vars = Price`
BestETS <- choochoo %>%
   model('Auto' = ETS(Price)) %>%
   forecast(h = 82)

plot5 <- BestETS %>%
   autoplot() +
   autolayer(milk) +
  labs(title = "ETS Auto")
## Plot variable not specified, automatically selected `.vars = Price`
BestETS2 <- choochoo %>%
   model('Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N"))) %>%
   forecast(h = 82)
 
plot6 <- BestETS2 %>%
   autoplot() +
   autolayer(milk) +
  labs(title = "ETS Multi")
## Plot variable not specified, automatically selected `.vars = Price`
plot_grid(plot1, plot2, plot3, plot4, plot5, plot6)

RMSE values

# Drift = 0.3337870 (Best) # ARIMA (012) = 0.3551251 (5th) # ARIMA (212) = 0.3548632 (3rd) # ARIMA (011) = 0.3548885 (4th) # ETS Auto = 0.3568725 (Last) # ETS Multi = 0.3358933 (2nd)

MAPE values

# Drift = 8.321966 (Last) # ARIMA (012) = 8.124391 (5th) # ARIMA (212) = 8.000688 (2nd) # ARIMA (011) = 8.024482 (3rd) # ETS Auto = 7.662227 (Best) # ETS Multi = 8.036521 (4th)

Resid check for models

Drift

Milkdrift2 <- milk %>%
  model('Drift' = RW(Price ~ drift()))

Milkdrift2 %>%
  gg_tsresiduals() +
  labs(title = "Drift resid")
## Warning: Removed 1 row(s) containing missing values (geom_path).
## Warning: Removed 1 rows containing missing values (geom_point).
## Warning: Removed 1 rows containing non-finite values (stat_bin).

augment(Milkdrift2) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model lb_stat lb_pvalue
##   <chr>    <dbl>     <dbl>
## 1 Drift     35.4  0.000108

ARIMA (0,1,2)

AR012 <- milk %>% 
  model('012' = ARIMA(Price ~ pdq(0,1,2)))

AR012 %>%
  gg_tsresiduals() +
  labs(title = "ARIMA (0,1,2) resid")

augment(AR012) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model lb_stat lb_pvalue
##   <chr>    <dbl>     <dbl>
## 1 012       9.72     0.465

ARIMA (0,1,1)

AR011 <- milk %>% 
  model('011' = ARIMA(Price ~ pdq(0,1,1)))

AR011 %>%
  gg_tsresiduals() +
  labs(title = "ARIMA (0,1,1) resid")

augment(AR011) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model lb_stat lb_pvalue
##   <chr>    <dbl>     <dbl>
## 1 011       10.9     0.369

ARIMA (2,1,2)

AR212 <- milk %>% 
  model('212' = ARIMA(Price ~ pdq(2,1,2)))

AR212 %>%
  gg_tsresiduals() +
  labs(title = "ARIMA (2,1,2) resid")

augment(AR212) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model lb_stat lb_pvalue
##   <chr>    <dbl>     <dbl>
## 1 212       4.20     0.938

ETS Auto

AutoETS <- milk %>%
  model('Auto' = ETS(Price))

AutoETS %>%
  gg_tsresiduals() +
  labs(title = "ETS Auto")

augment(AutoETS) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model lb_stat lb_pvalue
##   <chr>    <dbl>     <dbl>
## 1 Auto      35.5  0.000101

ETS Multi

MultiETS <- milk %>%
  model('Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N")))

MultiETS %>%
  gg_tsresiduals() +
  labs(title = "ETS Multi")

augment(MultiETS) %>%
  features(.resid, ljung_box, lag = 10, dof = 0)
## # A tibble: 1 × 3
##   .model               lb_stat lb_pvalue
##   <chr>                  <dbl>     <dbl>
## 1 Multiplicative Trend    35.7 0.0000939

Ljung box p-values

# Drift = 0.0001077292 (Not WN) # ARIMA (012) = 0.4652559 (Is WN) # ARIMA (212) = 0.9376757 (Is WN) # ARIMA (011) = 0.3686044 (Is WN) # ETS Auto = 0.000101258 (Not WN)
# ETS Multi = 0.00009387677 (Not WN)

Plot of all Forecasts

Milkdriftcast <- milk %>%
  model('Drift' = RW(Price ~ drift())) %>%
  forecast(h = 12)

plot11 <- Milkdriftcast %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "Drift")
## Plot variable not specified, automatically selected `.vars = Price`
bestfrcast <- milk %>% 
  model('012' = ARIMA(Price ~ pdq(0,1,2))) %>%
  forecast(h = 12)

plot22 <- bestfrcast %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (0,1,2)")
## Plot variable not specified, automatically selected `.vars = Price`
bestfr3cast <- milk %>% 
  model('212' = ARIMA(Price ~ pdq(2,1,2))) %>%
  forecast(h = 12)

plot33 <- bestfr3cast %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (2,1,2)")
## Plot variable not specified, automatically selected `.vars = Price`
bestfr4cast <- milk %>% 
  model('011' = ARIMA(Price ~ pdq(0,1,1))) %>%
  forecast(h = 12)

plot44 <- bestfr4cast %>%
  autoplot() +
  autolayer(milk) +
  labs(title = "ARIMA (0,1,1)")
## Plot variable not specified, automatically selected `.vars = Price`
BestETScast <- milk %>%
   model('Auto' = ETS(Price)) %>%
   forecast(h = 12)

plot55 <- BestETScast %>%
   autoplot() +
   autolayer(milk) +
  labs(title = "ETS Auto")
## Plot variable not specified, automatically selected `.vars = Price`
BestETS2cast <- milk %>%
   model('Multiplicative Trend' = ETS(Price ~ error("A") + trend("M") + season("N"))) %>%
   forecast(h = 12)
 
plot66 <- BestETS2cast %>%
   autoplot() +
   autolayer(milk) +
  labs(title = "ETS Multi")
## Plot variable not specified, automatically selected `.vars = Price`
plot_grid(plot11, plot22, plot33, plot44, plot55, plot66)

Cross vally

ModCompfr <- milk %>%
  stretch_tsibble(.init = 10) %>%
  model(
    "Multiplicative Trend" = ETS(Price ~ error("A") + trend("M") + season("N")),
    'Auto' = ETS(Price),
    '212' = ARIMA(Price ~ pdq(2,1,2)),
    '012' = ARIMA(Price ~ pdq(0,1,2)),
    '011' = ARIMA(Price ~ pdq(0,1,1)),
    'Drift' = RW(Price ~ drift())) %>%
  forecast(h = 1)
## Warning: It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## Warning in sqrt(diag(best$var.coef)): NaNs produced
## Warning: It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## It looks like you're trying to fully specify your ARIMA model but have not said if a constant should be included.
## You can include a constant using `ARIMA(y~1)` to the formula or exclude it by adding `ARIMA(y~0)`.
## Warning: 11 errors (1 unique) encountered for 212
## [11] Could not find an appropriate ARIMA model.
## This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
## For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots
## Warning: 4 errors (1 unique) encountered for 012
## [4] Could not find an appropriate ARIMA model.
## This is likely because automatic selection does not select models with characteristic roots that may be numerically unstable.
## For more details, refer to https://otexts.com/fpp3/arima-r.html#plotting-the-characteristic-roots
accuracy(ModCompfr, milk)
## Warning: The future dataset is incomplete, incomplete out-of-sample data will be treated as missing. 
## 1 observation is missing at 2022 Nov
## # A tibble: 6 × 10
##   .model            .type      ME   RMSE    MAE    MPE  MAPE  MASE RMSSE    ACF1
##   <chr>             <chr>   <dbl>  <dbl>  <dbl>  <dbl> <dbl> <dbl> <dbl>   <dbl>
## 1 011               Test  0.00448 0.0722 0.0512 0.115   1.55 0.195 0.215 0.0563 
## 2 012               Test  0.00366 0.0729 0.0512 0.0935  1.55 0.196 0.217 0.0327 
## 3 212               Test  0.00401 0.0681 0.0505 0.102   1.52 0.193 0.203 0.00678
## 4 Auto              Test  0.00526 0.0745 0.0510 0.138   1.55 0.195 0.222 0.299  
## 5 Drift             Test  0.00145 0.0746 0.0508 0.0169  1.53 0.194 0.222 0.313  
## 6 Multiplicative T… Test  0.00109 0.0770 0.0530 0.0118  1.61 0.203 0.229 0.320

Best Model:

HiLo PI for best Model

Price of Milk in 12 periods

E

F

G

H