library(astsa) # acf2() e sarima()
library(forecast) # forecast() e autoplot()
library(ggplot2) # carregar temas para a função autoplot()
Introdução
Neste post apresento como estimar um modelo SARIMA simples no R usando os pacotes astsa
e forecast
. O pacote astsa
foi elaborado pelos autores do livro Time Series Analysis. Já o forecast
foi desenvolvido por Rob. Hyndman, autor do livro Forecasting: Principles and Practice.
O modelo
O SARIMA adiciona sazonalidade estocástica multiplicativa ao modelo ARIMA. Lembre-se que o modelo ARMA(p,q) geral é da forma: \[\begin{equation} y_{t} = \mu + \phi_{1}y_{t-1} + \phi_{2}y_{t-2} + \dots + \phi_{p}y_{t-p} + \epsilon_{t} + \theta_{1}\epsilon_{t-1} + \theta_{2}\epsilon_{t-2} + \dots + \theta_{q}\epsilon_{t-q} \end{equation}\] Podemos reescrever a expressão acima usando o operador defasagem e os polinômios \[\begin{align} \phi(L) & = 1-\phi_{1}L - \phi_{2}L^{2} - \dots - \phi_{p}L^{p} \\ \theta(L) & = 1+\theta_{1}L + \theta_{2}L^{2} + \dots + \theta_{q}L^{q} \end{align}\] Assim a expressão pode ser resumida como \[\begin{equation} \phi(L)y_{t} = \mu + \theta(L)\epsilon_{t} \end{equation}\] O modelo ARIMA tira diferenças \(d\) de \(y_{t}\): \[\begin{equation} \phi(L)(1-L)^{d}y_{t} = \theta(L)\epsilon_{t} \end{equation}\] A princípio não há restrição sobre o valor de \(d\) mas na prática sabemos que \(d = 1\) ou \(d = 2\). Agora vamos definir os polinômios sazonais: \(\Phi(L^{s}) = 1-\Phi_{1}L^{s} - \Phi_{2}L^{2s} - \dots - \Phi_{p}L^{Ps}\) e \(\Theta(L^{s}) = 1+\Theta_{1}L^{s} + \Theta_{2}L^{2s} + \dots + \Theta_{q}L^{Qs}\). Ou seja, digamos que a série \(y_{t}\) seja mensurada mensalmente e apresente alguma sazonalidade anual. \(y_{t}\) pode ser, por exemplo, a receita de vendas de uma empresa de varejo. É provável que a receita no mês de dezembro seja mais alta que a média do ano. Como isto se repete todo ano vamos encontrar um padrão anual nos dados que deve ser modelado. Neste exemplo, como \(y_{t}\) é mensal temos que \(s = 12\) nos polinômios sazonais. Logo teremos que: \[\begin{align} \Phi(L)y_{t} & = 1 + \Phi_{1}y_{t-12} + \Phi_{2}y_{t-24} + \dots + \Phi_{p}y_{t-Ps}\\ \Theta(L)\epsilon_{t} & = 1+\Theta_{1}\epsilon_{t-12} + \Theta_{2}\epsilon_{t-24} + \dots + \Theta_{q}\epsilon_{t-Qs} \end{align}\] Podemos então montar modelos SARMA(p,q)(P,Q)[s]. Para tornar a notação mais clara vamos escrever o caso em que \(y_{t} \sim \text{SARMA}(2,1)(1,3)[12]\) \[\begin{equation} y_{t} = \phi_{1}y_{t-1} + \phi_{2}y_{t-2} + \Phi_{1}y_{t-12} + \epsilon_{t} + \theta_{1}\epsilon_{t-1} + \Theta_{1}\epsilon_{t-12} + \Theta_{2}\epsilon_{t-24} + \Theta_{3}\epsilon_{t-36} \end{equation}\] De forma geral, temos que: \[\begin{equation} \Phi(L^{s})\phi(L)y_{t} = \mu + \theta(L^{s})\theta(L)\epsilon_{t} \end{equation}\] Finalmente, de forma análoga ao ARIMA, tiramos diferenças sazonais \(D\) para chegar em: \[\begin{equation} \Phi(L^{s})\phi(L)(1-L)^{d}(1-L^{s})^{D}y_{t} = \mu + \theta(L^{s})\theta(L)\epsilon_{t} \end{equation}\] Assim, como na análise do ARIMA o procedimento para identificação parte da comparação entre o correlograma empírico com as correlações teóricas do processo SARMA(p,q)(P,Q)[s]. Digamos, por exemplo, que a série de interesse \(y_{t}\) siga um SARIMA(p,0,q)(P,1,Q). Então, temos que
\[ x_{t} = (1-L^{12})y_{t} \]
a série diferenciada sazonalmente, segue um SARMA(p,q)(P,Q). A partir das funções de autocorrelação e autocorrelação parcial de \(x_{t}\) podemos tentar recuperar o verdadeiro valor de \(p\), \(q\), \(P\) e \(Q\).
Exemplo: passagens aéreas
Primeiro vamos carregar os pacotes.
Vamos analisar o comportamento da demanda por passagens aéreas internacionais. Este é um exemplo clássico estudado em Box & Jenkins (1976). Esta séria apresenta tanto uma tendência de crescimento como uma clara sazonalidade.
autoplot(AirPassengers) +
geom_point(shape = 21) +
labs(x = "", y = "Passagens aéreas (milhares)",
title = "Demanda mensal de passagens aéreas internacionais",
caption = "Fonte: Box & Jenkins (1976)") +
theme_bw()
Uma maneira interessante de visualizar a sazonalidade da série é “empilhando” os ciclos. No caso, parece haver uma sazonalidade que se repete a cada ano. A função ggseasonplot
do pacote forecast
facilita esta visualização. Os meses de junho, julho e agosto parecem sempre ter valores mais elevados. De fato, estes são os meses do verão no hemisfério norte e da alta estação do turismo.
# Gráfico mensal
ggseasonplot(AirPassengers) +
ggtitle("Sazonalidade") +
labs(x = "") +
scale_colour_discrete(name = "Ano") +
theme_bw()
Identificação e tranformações
Como a variância da série cresce ao longo do tempo aplico uma transformação log nos valores da série. Seja \(y_{t}\) nossa série. Então fazemos \(ly_{t} \equiv \text{log}(y_{t}).\)
# Aplica transformação log (logaritmo natural)
<- log(AirPassengers)
ly # Gráfico
autoplot(ly) +
geom_point(shape = 21) +
labs(x = "",
y = "Passagens aéreas (log)",
title = "Demanda mensal de passagens aéreas internacionais") +
theme_bw()
Para testar a acurácia do modelo vamos remover algumas das últimas observações. Estas serão testadas contra as previsões do modelo. Aqui sigo a nomenclatura de train (treino) e test (teste). O gráfico abaixo permite visualizar esta divisão, onde os valores em vermelho foram excluídos da nossa amostra.
<- window(ly, end = c(1957, 12))
train <- window(ly, start = c(1958, 1))
test
autoplot(train) +
autolayer(test) +
theme_bw() +
theme(legend.position = "bottom")
Tirando a primeira diferença da série removemos a sua tendência de crescimento. Fazemos \(dly_{t} \equiv (1-L)ly_{t} = ly_{t} - ly_{t-1}\) usando a função diff()
. Note pelo gráfico que ainda parece haver forte sazonalidade na série.
# Tira a primeira diferença da série
<- diff(train)
dly
autoplot(dly) +
ggtitle("Primeira diferença do log da série") +
theme_bw()
Podemos ver isto mais claramente na análise do correlograma da série diferenciada. No gráfico abaixo, os “lags” seguem a periodicidade da série, isto é, cada “lag” representa o equivalente a 12 meses. Parece haver uma forte correlação entre \(dly_{t}\) com \(dly_{t-12}, dly_{t-24}, \dots, dly_{t-12k}\) com \(k = 1, 2, \dots\).
# Gráfico da FAC e FACP
acf2(dly)
Agora tiramos uma diferença sazonal de 12 meses. A série resultante, \(sdly_{t}\) fica: \[\begin{align} sdly_{t} \equiv (1-L^{12})dly_{t} & = (1-L^{12})(1-L)ly_{t} \\ & = (1 - L^{12} - L + L^{13})ly_{t} \\ & = ly_{t} - ly_{t-1} - ly_{t-12} + ly_{t-13} \end{align}\]
Abaixo temos o correlograma de \(sdly_{t}\). A primeira defasagem é significativa tanto no ACF como no PACF. Além disso a 12ª defasagem também é significativa em ambos. Assim, vamos primeiro tentar um modelo de “ordem máxima” SARIMA\((1,1,1)(1,1,1)[12]\). A partir deste modelo, vamos tentar estimar outros de ordens mais baixas para evitar o problema de sobreparametrização.
# Tira a primeira diferença sazonal da série
<- diff(dly, 12)
sdly # Gráfico da FAC e FACP
acf2(sdly)
Estimação
A equação do modelo que estamos estimando é: \[
(1 - \phi L)(1 - \Phi L^{12})(1 - L)(1 - L^{12})ly_{t} = (1 + \theta L)(1 + \Theta L^{12})\epsilon_{t}
\] Vamos o usar o comando sarima
do pacote astsa
. Note que os resíduos do modelo parecem se comportar como ruído branco, indicando que nosso modelo está bem ajustado.
<- sarima(p = 1, d = 1, q = 1, P = 1, D = 1, Q = 1, S = 12, xdata = train)) (m1
initial value -3.031632
iter 2 value -3.170590
iter 3 value -3.246467
iter 4 value -3.246688
iter 5 value -3.247295
iter 6 value -3.247539
iter 7 value -3.247841
iter 8 value -3.248283
iter 9 value -3.248794
iter 10 value -3.249345
iter 11 value -3.249360
iter 12 value -3.249378
iter 13 value -3.249379
iter 14 value -3.249380
iter 15 value -3.249381
iter 16 value -3.249381
iter 17 value -3.249381
iter 17 value -3.249381
iter 17 value -3.249381
final value -3.249381
converged
initial value -3.248923
iter 2 value -3.252561
iter 3 value -3.258267
iter 4 value -3.265627
iter 5 value -3.267071
iter 6 value -3.267585
iter 7 value -3.267745
iter 8 value -3.268029
iter 9 value -3.268401
iter 10 value -3.268494
iter 11 value -3.268499
iter 12 value -3.268506
iter 13 value -3.268511
iter 14 value -3.268511
iter 15 value -3.268511
iter 15 value -3.268511
iter 15 value -3.268511
final value -3.268511
converged
$fit
Call:
arima(x = xdata, order = c(p, d, q), seasonal = list(order = c(P, D, Q), period = S),
include.mean = !no.constant, transform.pars = trans, fixed = fixed, optim.control = list(trace = trc,
REPORT = 1, reltol = tol))
Coefficients:
ar1 ma1 sar1 sma1
0.2565 -0.6243 -0.0599 -0.5574
s.e. 0.2759 0.2289 0.1765 0.1670
sigma^2 estimated as 0.001367: log likelihood = 175.71, aic = -341.42
$degrees_of_freedom
[1] 91
$ttable
Estimate SE t.value p.value
ar1 0.2565 0.2759 0.9299 0.3549
ma1 -0.6243 0.2289 -2.7271 0.0077
sar1 -0.0599 0.1765 -0.3392 0.7353
sma1 -0.5574 0.1670 -3.3380 0.0012
$AIC
[1] -3.593882
$AICc
[1] -3.589203
$BIC
[1] -3.459467
Para evitar o problema de sobreparametrização (overfitting) temos que tentar ajustar modelos de ordens similares, porém mais baixas. Este processo costuma ser iterativo, isto é, na base da tentativa e erro seguindo algum critério de informação (i.e.: AIC, AICc, BIC, etc.). Depois de tentar vários modelos diferentes chegamos, por exemplo, no SARIMA\((0,1,1)(0,1,1)[12]\). A equação do modelo pode ser expressa como: \[ (1 - L)(1 - L^{12})ly_{t} = (1 + \theta L)(1 + \Theta L^{12})\epsilon_{t} \]
Novamente, os resíduos do modelo indicam que ele está bem ajustado aos dados.
<- sarima(p = 0, d = 1, q = 1, P = 0, D = 1, Q = 1, S = 12, xdata = train) m2
initial value -3.047456
iter 2 value -3.251231
iter 3 value -3.251514
iter 4 value -3.268396
iter 5 value -3.270049
iter 6 value -3.270196
iter 7 value -3.270197
iter 8 value -3.270198
iter 8 value -3.270198
iter 8 value -3.270198
final value -3.270198
converged
initial value -3.263453
iter 2 value -3.264133
iter 3 value -3.264159
iter 4 value -3.264160
iter 4 value -3.264160
iter 4 value -3.264160
final value -3.264160
converged
$fit m2
Call:
arima(x = xdata, order = c(p, d, q), seasonal = list(order = c(P, D, Q), period = S),
include.mean = !no.constant, transform.pars = trans, fixed = fixed, optim.control = list(trace = trc,
REPORT = 1, reltol = tol))
Coefficients:
ma1 sma1
-0.3864 -0.5885
s.e. 0.1097 0.0927
sigma^2 estimated as 0.001383: log likelihood = 175.3, aic = -344.59
A estimativa tem a forma:
\[ (1 - L)(1 - L^{12})ly_{t} = (1 - 0.3864 L)(1 - 0.5885 L^{12})\epsilon_{t} \]
Previsão
Para computar as previsões do modelo usamos a função sarima.for
. Aqui podemos comparar as previsões do modelo (construindo usando apenas as observações dentro de train) com as observações. Esta função automaticametne retorna um gráfico com as previsões fora da amostra.
<- sarima.for(train, n.ahead = length(test),
pred p = 0, d = 1, q = 1, P = 0, D = 1, Q = 1, S = 12)
Podemos construir um gráfico que inclui as observações reservadas no test usando as seguintes funções base do R.
plot.ts(pred$pred, col = "red", ylim = c(5.7, 6.5),
ylab = "",
main = "Previsão do modelo SARIMA(0, 1, 1)(0, 1, 1)[12]")
lines(test, type = "o")
legend("topleft", legend = c("Previsto", "Observado"), lty = 1,
pch = c(NA, 1), col = c("red", "black"))
Como comentado em outro post, pode-se produzir visualizações mais elegantes usando o ggplot2
, mas o pacote não “conversa” bem com os objetos típicos de séries de tempo. Um jeito de contornar isto é usando a função autoplot()
do pacote forecast
. Abaixo, reestimo o modelo usando a função arima
. Este passo é necessário para usar a função autoplot()
.
<- arima(train, order = c(0, 1, 1), seasonal = list(order = c(0, 1, 1), period = 12)) m
Agora podemos verificar a qualidade das nossas previsões. O gráfico abaixo foi construído funções base do R. A linha azul representa as previsões do modelo SARIMA especificado acima, enquanto que a linha vermelha representa as observações. As áreas sombreadas são intervalos de confiança: o mais escuro é de 80% e o mais claro de 95%.
plot(forecast(m, h = length(test)))
lines(test, col = "black")
legend("topleft",
legend = c("Previsto", "Observado"),
lty = 1, col = c("red", "black"))
Usando o autoplot temos o seguinte resultado:
autoplot(forecast(m, h = length(test)), include = 50) +
autolayer(test) +
labs(x = "", y = "") +
theme_bw() +
theme(legend.position = "none")
Conclusão
Os modelos SARIMA são uma extensão aos modelos ARIMA que acrescentam um componente sazonal nos dados. O exemplo acima mostrou uma série com frequência mensal, mas a mesma lógica se aplica a séries com sazonalidade semestral, trimestral, etc.
Estes modelos geram boas previsões e são fáceis de estimar o que explica a sua popularidade.