Ряд данных (IM_T_M) содержит месячные показатели импорта товаров в млрд долларов.
dygraph(IM, main = "Импорт товаров",
xlab = "Год",
ylab = "млрд. долл.") %>%
dyLegend(show = "follow") %>%
dyRangeSelector()
Временной ряд для импорта товаров является нестационарным из-за наличия как тренда, так и сезонности.В целом, график выглядит довольно “аккуратно”, выбросов на нем не наблюдается.
Проверим ряд на наличие пропущенных значений:
sum(is.na(IM))
## [1] 0
Разложим рассмариваемый ряд на составные части:
autoplot(stl(IM, s.window="periodic")) +
labs(title = "Декомпозиция временного ряда",
x = "Время")
После визуального анализа самого ряда данных и его составляющих можно заключить, что ряд делают нестационарным три основные вещи: 1) тренд, 2) сезонность, 3) изменение дисперсии сезонного размаха.
Чтобы убедиться, стационарен рассматриваемый ряд или нет, применим статистический критерий Дики-Фуллера. В качестве нулевой гипотезы будет рассматривается гипотеза о нестационарности ряда, в качестве альтернативы - то, что ряд стационарен. Соответственно, отвергнуть гипотезу о нестационарности временного ряда можно в том случае, если p-value < 0.05 (при 95% уровне значимости).
Критерий Дики-Фуллера:
Нулевая гипотеза: H0: ряд нестационарен;
Альтернативная гипотеза: H1: ряд стационарен.
adf.test(IM, k=12, alternative = "stationary")
##
## Augmented Dickey-Fuller Test
##
## data: IM
## Dickey-Fuller = -3.1876, Lag order = 12, p-value = 0.09034
## alternative hypothesis: stationary
Тест показал, что ряд нестационарен.
Для того чтобы сгладить увеличение размаха сезонных колебаний временного ряда, будет использоваться логарифмирование.
lnpml <- log(IM)
autoplot(lnpml) +
labs(title = "Логарифм импорта") +
xlab("Год")
adf.test(lnpml, k = 12, alternative = "stationary")
##
## Augmented Dickey-Fuller Test
##
## data: lnpml
## Dickey-Fuller = -2.5749, Lag order = 12, p-value = 0.3339
## alternative hypothesis: stationary
На графике видно, что размах колебаний стал ровнее, чем был, но по критерию Дики-Фуллера ряд все равно не стационарен.
Чтобы привести ряд к стационарному виду, начнем с применения сезонного дифференцирования.
deseasonal <- diff(lnpml, lag = 12, differences = 1)
autoplot(deseasonal,
main = "Логарифм импорта без сезонной компоненты", xlab = "Год")
adf.test(deseasonal, alternative = "stationary")
## Warning in adf.test(deseasonal, alternative = "stationary"): p-value
## smaller than printed p-value
##
## Augmented Dickey-Fuller Test
##
## data: deseasonal
## Dickey-Fuller = -4.5033, Lag order = 6, p-value = 0.01
## alternative hypothesis: stationary
После сезонного дифференцирования на графике уже не видно “сезонного паттерна”, а p-value критерия Дики-Фуллера значительно уменьшилось.
detrended <- diff(deseasonal, lag = 1, differences = 1)
autoplot(detrended,
main = "Логарифм импорта без сезонной компоненты", xlab = "Год")
adf.test(detrended, alternative = "stationary")
## Warning in adf.test(detrended, alternative = "stationary"): p-value smaller
## than printed p-value
##
## Augmented Dickey-Fuller Test
##
## data: detrended
## Dickey-Fuller = -5.5273, Lag order = 6, p-value = 0.01
## alternative hypothesis: stationary
На графике все же есть выброс, остносящийся к кризису 1998 года, которые построенной моделью плохо описывается.
На графике виден стационарный ряд, что подкрепляется результатом теста Дики-Фуллера. По итогу сезонного и простого дифференцирование были подобраны два параметра для модели ARIMA: d и D.
D - это порядок сезонного дифференцирования = 1;
d - это порядок обычного дифференцирования = 1. Можно рассматривать и d = 0, так как уже после сезонного дифференцирования ряд был стационарным.
Для подобора приближенных остальных параметров модели обычно пользуются коррелограммами, показывающими автокорреляцию и частичную автокорреляцию.
Acf(detrended, lag.max = 48, main='Автокорреляция дифференцированного ряда')
Pacf(detrended, lag.max = 48, main='Частичная автокорреляция дифференцированного ряда')
Q - номер последнего сезонного лага (по порядку) со значимой автокорреляцией −> Q = 1 (сезонный период длиной 12)
q - номер последнего несезонного лага со значимой автокорреляцией −> q = 4
P - номер последнего сезонного лага со значимой частичной автокорреляцией −> P = 2
p - номер последнего незезонного лага со значимой частичной автокорреляцией −> p = 3
Обучим модель:
fit1 <- auto.arima(lnpml, d = 0, D = 1, max.p = 3, max.q = 4, max.P = 2,
max.Q = 1, max.order = 10, seasonal = TRUE, ic = "aic", stepwise = FALSE,
parallel = FALSE)
fit1
## Series: lnpml
## ARIMA(3,0,2)(2,1,1)[12]
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1 sar2 sma1
## -0.1310 0.1406 0.9501 1.0234 0.8984 0.1258 -0.0497 -0.7899
## s.e. 0.0475 0.0286 0.0327 0.0781 0.0370 0.0954 0.0845 0.0770
##
## sigma^2 estimated as 0.005609: log likelihood=332.7
## AIC=-647.4 AICc=-646.75 BIC=-614.5
Попробуем самостоятельно методом перебора подобрать сезонные параметры. Порядок дифференцирования уже подобран - 1, для двух остальных пройдемся циклом от нуля до максимального значения, найденного по кореллограммам.
for (P in c(0, 1, 2)){
for (Q in c(0, 1)){
cat("P=",P,"Q=",Q)
print(arima(lnpml, order = c(3, 0, 2),
seasonal = list(order = c(P, 1, Q), period = 12)))
}
}
## P= 0 Q= 0
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2
## -0.2804 0.3758 0.7553 1.1475 0.6458
## s.e. 0.1400 0.1298 0.0818 0.1809 0.1022
##
## sigma^2 estimated as 0.008013: log likelihood = 283.21, aic = -554.42
## P= 0 Q= 1
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sma1
## -0.1367 0.1391 0.9547 1.0242 0.8979 -0.7443
## s.e. 0.0396 0.0246 0.0278 0.0709 0.0340 0.0528
##
## sigma^2 estimated as 0.005534: log likelihood = 330.8, aic = -647.59
## P= 1 Q= 0
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1
## -0.1818 0.1189 0.9512 1.0708 0.9421 -0.3861
## s.e. 0.0252 0.0210 0.0206 0.0465 0.0238 0.0551
##
## sigma^2 estimated as 0.006553: log likelihood = 310.07, aic = -606.15
## P= 1 Q= 1
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1 sma1
## -0.1767 0.1470 0.9861 1.1566 0.9966 0.1679 -0.8295
## s.e. 0.0094 0.0108 0.0092 NaN NaN 0.0819 0.0578
##
## sigma^2 estimated as 0.00535: log likelihood = 333.33, aic = -650.65
## P= 2 Q= 0
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1 sar2
## -0.1725 0.1259 0.9572 1.0641 0.9289 -0.4966 -0.2563
## s.e. 0.0275 0.0201 0.0212 0.0599 0.0293 0.0590 0.0590
##
## sigma^2 estimated as 0.006122: log likelihood = 319.03, aic = -622.06
## P= 2 Q= 1
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(P, 1, Q), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1 sar2 sma1
## -0.1310 0.1406 0.9501 1.0234 0.8984 0.1258 -0.0497 -0.7899
## s.e. 0.0475 0.0286 0.0327 0.0781 0.0370 0.0954 0.0845 0.0770
##
## sigma^2 estimated as 0.005452: log likelihood = 332.7, aic = -647.4
Ориентируясь на критерий информативности Акаике (он минимизируется), найлучшей моделью оказывается следующая: ARIMA(3,0,2)(1,1,1). Сохраним эту модель.
fit2 <- arima(lnpml, order = c(3, 0, 2),
seasonal = list(order = c(1, 1, 1), period = 12))
fit2
##
## Call:
## arima(x = lnpml, order = c(3, 0, 2), seasonal = list(order = c(1, 1, 1), period = 12))
##
## Coefficients:
## ar1 ar2 ar3 ma1 ma2 sar1 sma1
## -0.1767 0.1470 0.9861 1.1566 0.9966 0.1679 -0.8295
## s.e. 0.0094 0.0108 0.0092 NaN NaN 0.0819 0.0578
##
## sigma^2 estimated as 0.00535: log likelihood = 333.33, aic = -650.65
Наиболее корректно ограничить обучающий период 2017 годом. Для начала оценим, какая модель подбирается автоматический: мультипликативная ошибка, аддитивный тренд и мультипликативная сезонность.
IM_ob <- window(lnpml, end = c(2017, 6))
fit2_ob <- arima(IM_ob, order = c(3, 0, 2),
seasonal = list(order = c(1, 1, 1), period = 12))
IM_test <- window(lnpml, start = c(2017, 7))
autoplot(forecast(fit2_ob, h = 12), PI = F) + autolayer(IM_test)
Сравним выбранные на предыдущем этапе значения: ARIMA(3,0,2)(1,1,1); и те значения, которые были предложены автоматически: ARIMA(3,0,2)(2,1,1). На графике изобразим только тестовый период для максимальной наглядности.
fit2_ob_1 <- arima(IM_ob, order = c(3, 0, 2),
seasonal = list(order = c(1, 1, 1), period = 12))
fit2_ob_2 <- arima(IM_ob, order = c(3, 0, 2),
seasonal = list(order = c(2, 1, 1), period = 12))
IM_1 <- forecast(fit2_ob_1, h = 12)
IM_2 <- forecast(fit2_ob_2, h = 12)
autoplot(IM_test, series = "test") +
autolayer(IM_1, PI = F, series = "ARIMA(3,0,2)*(1,1,1)") +
autolayer(IM_2, PI = F, series = "ARIMA(3,0,2)*(2,1,1)")
По графику сложно оценить, какая из двух моделей более подходит для дальнейшего прогноза. Изучим остатки.
Суди по визуальному анализу остатков и по сравнению значения ошибок, модель ARIMA(3,0,2)*(2,1,1) незначительно лучше подходит для постоения прогноза.
tsdisplay(residuals(fit2_ob_1), lag.max = 48, main = "Остатки модели ARIMA(3,0,2)*(1,1,1)")
tsdisplay(residuals(fit2_ob_2), lag.max = 48, main = "Остатки модели ARIMA(3,0,2)*(2,1,1)")
rbind(accuracy(fit2_ob_1),
accuracy(fit2_ob_2)) %>%
as_tibble() %>%
round(4) %>%
mutate(`Метод` = c('ARIMA(3,0,2)*(1,1,1)',
'ARIMA(3,0,2)*(2,1,1)')) %>%
select(`Метод`, ME, MAPE, MPE)
## # A tibble: 2 x 4
## Метод ME MAPE MPE
## <chr> <dbl> <dbl> <dbl>
## 1 ARIMA(3,0,2)*(1,1,1) 0.0042 2.48 0.130
## 2 ARIMA(3,0,2)*(2,1,1) 0.0038 2.55 0.116
fit3 <- arima(lnpml, order = c(3, 0, 2),
seasonal = list(order = c(2, 1, 1), period = 12))
lnfcst <- forecast(fit3, h = 60)
autoplot(lnfcst, main = "Прогноз для логарифмированного ряда") +
labs(x = "Год",
fill = "Доверительный интервал")
Теперь надо привести прогноз в нужный вид, то есть провести операцию, обратную натуральному логарифмированию.
fcst <- exp(lnfcst$mean)
autoplot(IM, series = 'Оригинал') +
autolayer(fcst, series = 'Прогноз') +
scale_color_manual(values = c("#CC99FF", "#CC0000")) +
labs(y = 'млрд.$', x = NULL, title = 'Прогноз импорта на 5 лет (60 периодов)', color = NULL)
Источники
(Прогнозирование временных рядов с помощью модели ARIMA)[https://rawgit.com/postlogist/research-seminar/master/time-series/arima.html#введение]
(coursera)[https://ru.coursera.org/lecture/data-analysis-applications/vybor-arima-i-proghnozirovaniie-1lolc]