A diferencia de los métodos naive y de medias móviles, el suavizamiento exponencial le da más peso a observaciones más recientes.
library("tidyverse")
library("fpp2")
# create training and validation of the Google stock data
goog.train <- window(goog, end = 900)
goog.test <- window(goog, start = 901)
# create training and validation of the AirPassengers data
qcement.train <- window(qcement, end = c(2012, 4))
qcement.test <- window(qcement, start = c(2013, 1))
Este procedimiento es adecuado cuando los datos no tienen tendencia o patrón estacional. Los pesos de cada observación son determinados por un parametro de suavizamiento \(\alpha\). Para un conjunto de datos con \(T\) observaciones , calculamos el valor predicho \(\hat{y_{t+1}}\), el cual estará basado en \(y_{1}\) a través de \(y_{t}\) de la siguiente forma:
\[ \hat{y_{t+1}} = \alpha y_{t} + \alpha(1- \alpha)y_{t-1} + ... +\alpha(1-\alpha)^{t-1}y_{1} \] Donde \(0 < \alpha < 1\). Este \(\alpha\) puede ser visto como una tasa de aprendizaje. Valores cercanos a cero son considerados como aprendizaje lento ya que se da más peso a información histórica, mientra que valores cercanos a 1 son considerados como aprendizaje rápido porque el algoritmo da más peso a las observaciones recientes.
ses.goog <- ses(goog.train, alpha = .2, h = 100)
autoplot(ses.goog) + theme_classic()
Como se comentó antes, los modelos SES no son utiles cuando las series tienen tendencia o comportamiento estacional, por ello el pronóstico a 100 dias de la serie es una linea plana.
Podemos probar removiendo la tendencia de la series diferenciandola
goog.dif <- diff(goog.train)
autoplot(goog.dif)
ses.goog.dif <- ses(goog.dif, alpha = .2, h = 100)
autoplot(ses.goog.dif)
Podemos evaluar el desempeño del modelo en nuestro set de validación pero primero debemos diferenciarla, al igual que set con el que se entrenó el modelo.
goog.dif.test <- diff(goog.test)
accuracy(ses.goog.dif, goog.dif.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.01368221 9.317223 6.398819 99.97907 253.7069 0.7572009
## Test set 0.97219517 8.141450 6.117483 109.93320 177.9684 0.7239091
## ACF1 Theil's U
## Training set -0.05440377 NA
## Test set 0.12278141 0.9900678
Podemos hacer tuning de la tasa de aprendizaje \(\alpha\) buscando aquel parametro que disminuya el RMSE
# identify optimal alpha parameter
alpha <- seq(.01, .99, by = .01)
RMSE <- NA
for(i in seq_along(alpha)) {
fit <- ses(goog.dif, alpha = alpha[i], h = 100)
RMSE[i] <- accuracy(fit, goog.dif.test)[2,2]
}
# convert to a data frame and idenitify min alpha value
alpha.fit <- tibble(alpha, RMSE)
alpha.min <- filter(alpha.fit, RMSE == min(RMSE))
# plot RMSE vs. alpha
ggplot(alpha.fit, aes(alpha, RMSE)) +
geom_line() +
geom_point(data = alpha.min, aes(alpha, RMSE), size = 2, color = "blue")
Corremos el modelo con nuestro hiperparametro optimizado
# refit model with alpha = .05
ses.goog.opt <- ses(goog.dif, alpha = .05, h = 100)
# performance eval
accuracy(ses.goog.opt, goog.dif.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.01188991 8.939340 6.030873 109.97354 155.7700 0.7136602
## Test set 0.30483955 8.088941 6.028383 97.77722 112.2178 0.7133655
## ACF1 Theil's U
## Training set 0.01387261 NA
## Test set 0.12278141 0.9998811
# plotting results
p1 <- autoplot(ses.goog.opt) +
theme(legend.position = "bottom")
p2 <- autoplot(goog.dif.test) +
autolayer(ses.goog.opt, alpha = .5) +
ggtitle("Predicted vs. actuals for the test data set")
gridExtra::grid.arrange(p1, p2, nrow = 1)
El modelo SES no funciona tampoco con predicciones de un plazo largo que además tiene tendencia. Una alternativa es el método de holt, que captura la tendencia de los datos mientras aplica suavizamiento exponencial
El método de holt hace predicciones sobre datos con tendencia usando dos parametros de suavizamiento \(\alpha\) y \(\beta\) que corresponenden al componente de nivel y tendencia respectivamente.
Las predicciones con esta metodología es una combinación del nivel estimado en \(t (L_{t})\) y la estimación de la tendencia en \(t (T_{t})\)
\[ \hat{y_{T+1}} = L_{t} +kT_{t} \] Los valores de \(L_{t}\) y \(T_{t}\) son actualizados de la siguiente forma:
\[ L_{t} = \alpha y_{t} +\alpha(1- \alpha)(L_{t-1} + T_{t-1}) \] \[ T_{t} = \beta(L_{t} - L_{t-1}) +(1- \beta)T_{t-1} \]
La primera ecuación indica que el nivel del tiempo \(t\) es un promedio ponderado del valor actual en el tiempo \(t\) y el nivel en el periodo previo, ajustado por la tendencia.
La segunda ecuación indica que la tendencia en el tiempo \(t\) es un promedio ponderado de la tendencia en el periodo previo y la información más reciente del cambio de nivel. Similar al SES, \(\alpha\) y \(\beta\) están restringidos al rango 0-1.
Para capturar el la tendencia multiplicativa se hace un ajuste menor a las ecuacions anteriores:
\[ \hat{y_{T+1}} = L_{t} kT_{t} \]
\[ L_{t} = \alpha y_{t} +\alpha(1- \alpha)(L_{t-1} T_{t-1}) \]
\[ T_{t} = \beta(\frac{L_{t}}{L_{t-1}}) +(1- \beta)T_{t-1} \]
Podemos volver a aplicar el método de Holt para la serie de Google.
holt.goog <- holt(goog.train, h = 100)
autoplot(holt.goog)
El modelo se optimiza solo minimizando los criterios AIC y BIC, encontrando los parametros de suavizamiento que generan las mejores predicciones
holt.goog$model
## Holt's method
##
## Call:
## holt(y = goog.train, h = 100)
##
## Smoothing parameters:
## alpha = 0.9999
## beta = 1e-04
##
## Initial states:
## l = 401.1276
## b = 0.4091
##
## sigma: 8.8149
##
## AIC AICc BIC
## 10045.74 10045.81 10069.75
# errores de predicción
accuracy(holt.goog, goog.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.003332796 8.795267 5.821057 -0.01211821 1.000720 1.002452
## Test set 0.545744415 16.328680 12.876836 0.03013427 1.646261 2.217538
## ACF1 Theil's U
## Training set 0.03100836 NA
## Test set 0.87733298 2.024518
Sin embargo la optimzación está basada en el set de entrenamiento y no en el de testeo, asi que podemos realizar una optimización sobre el error de pronóstico
# identify optimal alpha parameter
beta <- seq(.0001, .5, by = .001)
RMSE <- NA
for(i in seq_along(beta)) {
fit <- holt(goog.train, beta = beta[i], h = 100)
RMSE[i] <- accuracy(fit, goog.test)[2,2]
}
# convert to a data frame and idenitify min alpha value
beta.fit <- data_frame(beta, RMSE)
## Warning: `data_frame()` is deprecated as of tibble 1.1.0.
## Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
beta.min <- filter(beta.fit, RMSE == min(RMSE))
# plot RMSE vs. alpha
ggplot(beta.fit, aes(beta, RMSE)) +
geom_line() +
geom_point(data = beta.min, aes(beta, RMSE), size = 2, color = "blue")
# new model with optimal beta
holt.goog.opt <- holt(goog.train, h = 100, beta = 0.0601)
# accuracy of first model
accuracy(holt.goog, goog.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.003332796 8.795267 5.821057 -0.01211821 1.000720 1.002452
## Test set 0.545744415 16.328680 12.876836 0.03013427 1.646261 2.217538
## ACF1 Theil's U
## Training set 0.03100836 NA
## Test set 0.87733298 2.024518
# accuracy of new optimal model
accuracy(holt.goog.opt, goog.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.01493114 8.960214 6.058869 -0.005524151 1.039572 1.043406
## Test set 21.41138275 28.549029 23.841097 2.665066997 2.988712 4.105709
## ACF1 Theil's U
## Training set 0.009696325 NA
## Test set 0.895371665 3.435763
Optimizar beta trae algunos efectos secundarios sobre los intervalos de confianza calculados ya que al incremental hace que la tasa de aprendizaje sea más rápida y por lo tanto causando más incertidumbre.
p1 <- autoplot(holt.goog) +
ggtitle("Original Holt's Model") +
coord_cartesian(ylim = c(400, 1000))
p2 <- autoplot(holt.goog.opt) +
ggtitle("Optimal Holt's Model") +
coord_cartesian(ylim = c(400, 1000))
gridExtra::grid.arrange(p1, p2, nrow = 1)
Para hacer predicciones sobre datos con tendencia y comportamiento estacional podemos usar el método holt winters. Este método se pude implementar de forma aditiva o mutliplicativa , donde la elección dependerá del dataset. El modelo aditivo es mejor cuando la tendencia estacional es de la misma magnitud a lo largo de todos los datos, mientras que el modelo aditivo es preferido cuando la magnitud de la estacionalidad cambia cuando el tiempo incrementa.
autoplot(decompose(qcement))
Para el modelo aditivo la ecuación es la siguiente:
\[ \hat{y_{t+1}} = L_{t} +kT_{t} + S_{t+k-m} \] El nivel \(L_{t}\), tendencia \(T_{t}\) y la estacionalidad \(S_{t}\) son actualizados a partir de las siguientes ecuaciones con sus correspondientes parametros:
\[ L_{t} = \alpha (y_{t}-S_{t-m}) + (1- \alpha)(L_{t-1} + T_{t-1}) \]
\[ T_{t} = \beta (L_{t}-L_{t-1}) + (1- \beta)T_{t-1} \]
\[ S_{t} = \gamma (y_{t} - L_{t}) + (1-\gamma)S_{t-m} \]
qcement.hw <- ets(qcement.train, model = "AAA")
autoplot(forecast(qcement.hw))
En este modelo podemos especificar el error, tendencia y estacionalidad de la serie de la siguiente forma:
En R, por ejemplo model = "AAN
, indica que el error y tencia es aditiva y no existe estacionalidad. Si queremos buscar el mejor modelo model = "ZZZ
summary(qcement.hw)
## ETS(A,A,A)
##
## Call:
## ets(y = qcement.train, model = "AAA")
##
## Smoothing parameters:
## alpha = 0.6418
## beta = 1e-04
## gamma = 0.1988
##
## Initial states:
## l = 0.4511
## b = 0.0075
## s = 0.0049 0.0307 9e-04 -0.0365
##
## sigma: 0.0854
##
## AIC AICc BIC
## 126.0419 126.8676 156.9060
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001463693 0.08393279 0.0597683 -0.003454533 3.922727 0.5912949
## ACF1
## Training set 0.02150539
checkresiduals(qcement.hw)
##
## Ljung-Box test
##
## data: Residuals from ETS(A,A,A)
## Q* = 20.288, df = 3, p-value = 0.0001479
##
## Model df: 8. Total lags used: 11
si revisamos los errores del modelo, tienden a crecer a medida que pasa el tiempo, por lo que la medida más apropiada podría ser de error multiplicativo.
qcement.f1 <- forecast(qcement.hw, h = 5)
# check accuracy
accuracy(qcement.f1, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001463693 0.08393279 0.05976830 -0.003454533 3.922727 0.5912949
## Test set 0.031362775 0.07144211 0.06791904 1.115342984 2.899446 0.6719311
## ACF1 Theil's U
## Training set 0.02150539 NA
## Test set -0.31290496 0.2112428
\[ \hat{y_{t+1}} = (L_{t} +kT_{t})S_{t+k-m} \] El nivel \(L_{t}\), tendencia \(T_{t}\) y la estacionalidad \(S_{t}\) son actualizados a partir de las siguientes ecuaciones con sus correspondientes parametros:
\[ L_{t} = \alpha (\frac{y_{t}}{S_{t-m}}) + (1- \alpha)(L_{t-1} + T_{t-1}) \]
\[ T_{t} = \beta (L_{t}-L_{t-1}) + (1- \beta)T_{t-1} \]
\[ S_{t} = \gamma (\frac{y_{t}}{L_{t}}) + (1-\gamma)S_{t-m} \]
qcement.hw2 <- ets(qcement.train, model = "MAM")
checkresiduals(qcement.hw2)
##
## Ljung-Box test
##
## data: Residuals from ETS(M,A,M)
## Q* = 23.433, df = 3, p-value = 3.281e-05
##
## Model df: 8. Total lags used: 11
# additive error, trend and seasonality
qcement.hw1 <- ets(qcement.train, model = "AAA")
qcement.f1 <- forecast(qcement.hw1, h = 5)
accuracy(qcement.f1, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001463693 0.08393279 0.05976830 -0.003454533 3.922727 0.5912949
## Test set 0.031362775 0.07144211 0.06791904 1.115342984 2.899446 0.6719311
## ACF1 Theil's U
## Training set 0.02150539 NA
## Test set -0.31290496 0.2112428
# multiplicative error, additive trend and seasonality
qcement.hw2 <- ets(qcement.train, model = "MAA")
qcement.f2 <- forecast(qcement.hw2, h = 5)
accuracy(qcement.f2, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.002700842 0.08387446 0.05912640 -0.3689856 3.811958 0.5849446
## Test set 0.018046198 0.06438073 0.05763575 0.5614035 2.493544 0.5701974
## ACF1 Theil's U
## Training set 0.001490172 NA
## Test set -0.344335948 0.1722734
# additive error and trend and multiplicative seasonality
qcement.hw3 <- ets(qcement.train, model = "AAM", restrict = FALSE)
qcement.f3 <- forecast(qcement.hw3, h = 5)
accuracy(qcement.f3, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.007542179 0.07818508 0.05640602 0.3985457 3.693964 0.5580315
## Test set 0.046793468 0.09456029 0.09241462 1.6559245 3.882433 0.9142688
## ACF1 Theil's U
## Training set -0.01243122 NA
## Test set -0.05070111 0.2737314
# multiplicative error, additive trend, and multiplicative seasonality
qcement.hw4 <- ets(qcement.train, model = "MAM")
qcement.f4 <- forecast(qcement.hw4, h = 5)
accuracy(qcement.f4, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.0009645886 0.07807481 0.05595930 -0.02540554 3.657772 0.5536120
## Test set 0.0164979314 0.08297241 0.07162921 0.37381187 3.064455 0.7086363
## ACF1 Theil's U
## Training set -0.03038457 NA
## Test set -0.06185361 0.1930288
qcement.hw5 <- ets(qcement.train, model = "ZZZ")
summary(qcement.hw5)
## ETS(M,A,M)
##
## Call:
## ets(y = qcement.train, model = "ZZZ")
##
## Smoothing parameters:
## alpha = 0.7664
## beta = 0.0131
## gamma = 1e-04
##
## Initial states:
## l = 0.49
## b = 0.0064
## s = 1.0298 1.0488 1.0151 0.9063
##
## sigma: 0.0477
##
## AIC AICc BIC
## 0.3161397 1.1418277 31.1802503
##
## Training set error measures:
## ME RMSE MAE MPE MAPE MASE
## Training set 0.0009645886 0.07807481 0.0559593 -0.02540554 3.657772 0.553612
## ACF1
## Training set -0.03038457
Podemos optimizar el parametro gamma, dado que la función ets
como vimos antes con otras funciones minimizar el RMSE, aIC, BIC de los errores del set de entrenamiento
gamma <- seq(0.01, 0.85, 0.01)
RMSE <- NA
for(i in seq_along(gamma)) {
hw.expo <- ets(qcement.train, "AAA", gamma = gamma[i])
future <- forecast(hw.expo, h = 5)
RMSE[i] = accuracy(future, qcement.test)[2,2]
}
error <- data_frame(gamma, RMSE)
minimum <- filter(error, RMSE == min(RMSE))
ggplot(error, aes(gamma, RMSE)) +
geom_line() +
geom_point(data = minimum, color = "blue", size = 2) +
ggtitle("gamma's impact on forecast errors",
subtitle = "gamma = 0.21 minimizes RMSE")
# previous model with additive error, trend and seasonality
accuracy(qcement.f1, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set 0.001463693 0.08393279 0.05976830 -0.003454533 3.922727 0.5912949
## Test set 0.031362775 0.07144211 0.06791904 1.115342984 2.899446 0.6719311
## ACF1 Theil's U
## Training set 0.02150539 NA
## Test set -0.31290496 0.2112428
# new model with optimal gamma parameter
qcement.hw6 <- ets(qcement.train, model = "AAA", gamma = 0.21)
qcement.f6 <- forecast(qcement.hw6, h = 5)
accuracy(qcement.f6, qcement.test)
## ME RMSE MAE MPE MAPE MASE
## Training set -0.001312025 0.08377557 0.05905971 -0.2684606 3.834134 0.5842847
## Test set 0.033492771 0.07148708 0.06775269 1.2096488 2.881680 0.6702854
## ACF1 Theil's U
## Training set 0.04832198 NA
## Test set -0.35877010 0.2202448
autoplot(qcement.f6)