Сезонность является одним из основных
компонентов временного ряда. Она играет ключевую роль в прогнозирование таких
временных рядов, как посещаемость и выручка розничных магазинов, так как это
род вида деятельности носит ярко выраженный сезонный характер. В этой статье мы
остановимся на методах и подходах к идентификации и классификации сезонных
моделей подобных временных рядов. Рассмотрим использование описательных
инструментов статистики, а также рассмотрим методы визуализации.
Типы сезонности
Рассматриваемые нами временные ряды
строго упорядочены, то есть всегда повторяются в одном и том же порядке (часы в
течение дня, дни недели - с понедельника по воскресенье, месяцы года - с января
по декабрь). И так от дня к дню, недели к недели и от года к году. Сезонность
существует всегда там, где определенный характер событий связан с определенным
периодом во временном ряду. Например посещаемость магазина по часам, по дням
недели в зависимости от времени года, по месяцам.
Сезонность можно разделить на две
категории :
одномерная сезонность: когда есть только
одна доминирующая сезонность, например месячная посещаемость магазинов, она
практически не изменяется от года к году
многомерная сезонность : если существует
более чем одна доминирующая сезонность, это посещаемость по часам - зависит от
сезона и дня недели, посещаемость по дням недели - зависит от сезона (летом -
влияние дачного сезона).
Для примеры мы будем продолжать
использовать сгенерированный набор данных по посещаемости розничного магазина
одежды, он представляет пример с одномерной (квартальный и месячный временной
ряд) и многомерной (дневной временной ряд) сезонностями.
Начнем с просмотра общей информации и
графиков.
Дневной временной ряд.
ts_info(ts_day)
## The ts_day series is a ts object with 1
variable and 1826 observations
## Frequency: 365.25
## Start time: 2015
## End time: 2019.99657768652
## Frequency: 365.25
## Start time: 2015
## End time: 2019.99657768652
ts_plot(ts_day,
title = "Дневная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Год",
Xgrid = TRUE,
Ygrid = TRUE)
title = "Дневная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Год",
Xgrid = TRUE,
Ygrid = TRUE)
Месячный временной ряд.
ts_info(ts_month)
## The ts_month series is a ts object with 1
variable and 60 observations
## Frequency: 12
## Start time: 2015 1
## End time: 2019 12
## Frequency: 12
## Start time: 2015 1
## End time: 2019 12
ts_plot(ts_month,
title = "Месячная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Годы",
Xgrid = TRUE,
Ygrid = TRUE)
title = "Месячная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Годы",
Xgrid = TRUE,
Ygrid = TRUE)
Квартальный временной
ряд.
ts_info(ts_qr)
## The ts_qr series is a ts object with 1
variable and 20 observations
## Frequency: 4
## Start time: 2015 1
## End time: 2019 4
## Frequency: 4
## Start time: 2015 1
## End time: 2019 4
ts_plot(ts_qr,
title = "Квартальная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Годы",
Xgrid = TRUE,
Ygrid = TRUE)
title = "Квартальная посещаемость розничного магазина одежды",
Ytitle = "Посетители",
Xtitle = "Годы",
Xgrid = TRUE,
Ygrid = TRUE)
Основное различие между
этими тремя временными рядами является их плотность (или зернистость) на
единицу цикла (который, в данном случае, является календарный год), так дневной
временной ряд имеет 1826 наблюдений за 5 циклов (или 25 × 365 + 1), в отличие
от 60 наблюдений в месячном и 20 наблюдений в квартальном временном ряду.
Наличие большего количества наблюдений за единицу цикла, то есть, данные
временных рядов высоких частот, может дать больше информации о поведении
временного ряда по сравнению с временным рядом более низкой частоты. Однако,
это добавляет дополнительную сложность, которая требует больших усилий в
процессе анализа.
Сезонный анализ с описательной статистикой
Описательная статистика
заключает в себя простой, но мощный метод, описания ключевых статистических
характеристик анализируемых данных. Этот метод основан на использовании таких
статистических показателей, как среднее, медиана, квантиль и стандартное
отклонение и их визуализацию с помощью таких графических инструментов как
бокс-плот и гистограммы.
Попробуем с помощью
описательных статистик ответить на несколько вопросов. Например, действительно
ли дневная сезонность (рабочие дни против выходных) отличается в зависимости от
сезона (весна, осень, зима, лето). Для этого возьмем наш исходный датафрейм.
head(df_day)
## # A tibble: 6 x 10
## ds year qr month wday sc_month sc_wday sc_hday trend
## <dttm> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2015-01-01 00:00:00 2015 Q1 1 4 1.15 0.73 0.42 1
## 2 2015-01-02 00:00:00 2015 Q1 1 5 1.15 0.83 1.25 2
## 3 2015-01-03 00:00:00 2015 Q1 1 6 1.15 1.7 1 3
## 4 2015-01-04 00:00:00 2015 Q1 1 7 1.15 1.69 1 4
## 5 2015-01-05 00:00:00 2015 Q1 1 1 1.15 0.67 2.71 5
## 6 2015-01-06 00:00:00 2015 Q1 1 2 1.15 0.68 2.19 6
## # ... with 1 more variable: visit <dbl>
## ds year qr month wday sc_month sc_wday sc_hday trend
## <dttm> <dbl> <chr> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 2015-01-01 00:00:00 2015 Q1 1 4 1.15 0.73 0.42 1
## 2 2015-01-02 00:00:00 2015 Q1 1 5 1.15 0.83 1.25 2
## 3 2015-01-03 00:00:00 2015 Q1 1 6 1.15 1.7 1 3
## 4 2015-01-04 00:00:00 2015 Q1 1 7 1.15 1.69 1 4
## 5 2015-01-05 00:00:00 2015 Q1 1 1 1.15 0.67 2.71 5
## 6 2015-01-06 00:00:00 2015 Q1 1 2 1.15 0.68 2.19 6
## # ... with 1 more variable: visit <dbl>
Добавим к нему еще
одну колонку обозначающую рабочий день (1) и выходной (0)
df_day$wh <- ifelse((df_day$wday>5 | df_day$sc_hday>1),0,1)
И выведем некоторые
описательные характеристики по рабочим и выходным дням : среднее количество
посетителей (mean_w и mean_h), коэффициент вариации
посетителей (cv_w и cv_h) и соотношение между
средним количеством посетителей в рабочий и выходной день (wh_rat)
df_day_summary <- df_day %>%
group_by(month) %>%
summarise(mean_h = mean(ifelse(wh==0,visit,NA),na.rm = TRUE),
mean_w = mean(ifelse(wh==0,NA,visit),na.rm = TRUE),
cv_h= round(sd(ifelse(wh==0,visit,NA),na.rm = TRUE)/mean_h,2),
cv_w= round(sd(ifelse(wh==0,NA,visit),na.rm = TRUE)/mean_w,2),
wh_rat=mean_w/mean_h)
df_day_summary
group_by(month) %>%
summarise(mean_h = mean(ifelse(wh==0,visit,NA),na.rm = TRUE),
mean_w = mean(ifelse(wh==0,NA,visit),na.rm = TRUE),
cv_h= round(sd(ifelse(wh==0,visit,NA),na.rm = TRUE)/mean_h,2),
cv_w= round(sd(ifelse(wh==0,NA,visit),na.rm = TRUE)/mean_w,2),
wh_rat=mean_w/mean_h)
df_day_summary
## # A tibble: 12
x 6
## month mean_h mean_w cv_h cv_w wh_rat
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 628. 260. 0.16 0.15 0.414
## 2 2 366. 157. 0.09 0.12 0.430
## 3 3 478. 205. 0.1 0.1 0.429
## 4 4 466. 197. 0.09 0.11 0.422
## 5 5 445. 203. 0.18 0.11 0.456
## 6 6 387. 233. 0.09 0.09 0.602
## 7 7 403. 238. 0.08 0.09 0.591
## 8 8 437. 258. 0.09 0.09 0.591
## 9 9 553. 223. 0.08 0.14 0.403
## 10 10 649. 256. 0.08 0.14 0.395
## 11 11 732. 287. 0.11 0.13 0.392
## 12 12 626. 268. 0.08 0.12 0.429
## month mean_h mean_w cv_h cv_w wh_rat
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 628. 260. 0.16 0.15 0.414
## 2 2 366. 157. 0.09 0.12 0.430
## 3 3 478. 205. 0.1 0.1 0.429
## 4 4 466. 197. 0.09 0.11 0.422
## 5 5 445. 203. 0.18 0.11 0.456
## 6 6 387. 233. 0.09 0.09 0.602
## 7 7 403. 238. 0.08 0.09 0.591
## 8 8 437. 258. 0.09 0.09 0.591
## 9 9 553. 223. 0.08 0.14 0.403
## 10 10 649. 256. 0.08 0.14 0.395
## 11 11 732. 287. 0.11 0.13 0.392
## 12 12 626. 268. 0.08 0.12 0.429
Видно, что отношение
среднего количества посетителей рабочий / выходной максимально летом (дачный
сезон) и минимально осенью.
Выведем это соотношение
графически
df_day %>%
group_by(month,wh) %>%
summarise(mean = mean(visit))%>%
ggplot(aes(x = as.factor(month), y = mean, fill = as.factor(wh))) +
geom_col(position = "dodge")+
scale_fill_discrete(name="раб./вых.",
labels=c("Вых. 0","Раб. 1"))+
labs(title="Средняя посещаемость магазина по рабочим и выходным дням", x="Месяц", y="Посетители")
group_by(month,wh) %>%
summarise(mean = mean(visit))%>%
ggplot(aes(x = as.factor(month), y = mean, fill = as.factor(wh))) +
geom_col(position = "dodge")+
scale_fill_discrete(name="раб./вых.",
labels=c("Вых. 0","Раб. 1"))+
labs(title="Средняя посещаемость магазина по рабочим и выходным дням", x="Месяц", y="Посетители")
Кроме того, видно, что
посещаемость в рабочие дни более стабильна, это подтверждает величина вариации,
выходные 23.3%, рабочие дни 15.9%.
df_day_summary %>%
group_by() %>%
summarise(cv_mean_h=sd(mean_h)/mean(mean_h),
cv_mean_w=sd(mean_w)/mean(mean_w))
group_by() %>%
summarise(cv_mean_h=sd(mean_h)/mean(mean_h),
cv_mean_w=sd(mean_w)/mean(mean_w))
## # A tibble: 1
x 2
## cv_mean_h cv_mean_w
## <dbl> <dbl>
## 1 0.233 0.159
## cv_mean_h cv_mean_w
## <dbl> <dbl>
## 1 0.233 0.159
Однако вспомним, что
сезонность отношение посещаемости в рабочие и выходные дни была задана
нами при формировании модели. Проверим, насколько использованные методы
правильно ее выявили, сравним входные и выходные показатели. Выведем заданное
соотношение по сезонам зима, весна, лето, осень.
wh_rat_0 <- round(apply(sc_day,1,function (x) mean(x[1:5])/mean(x[6:7])),2)
wh_rat_0
wh_rat_0
## [1] 0.43 0.42 0.59 0.40
Добавим ее к нашему
итоговому датафрейму и оценим разницу
df_day_summary$wh_rat_0 <- ifelse(df_day_summary$month %in% c(12,1,2),wh_rat_0[1],
ifelse(df_day_summary$month %in% c(3,4,5),wh_rat_0[2],
ifelse(df_day_summary$month %in% c(6,7,8),wh_rat_0[3],wh_rat_0[4])))
df_day_summary
ifelse(df_day_summary$month %in% c(3,4,5),wh_rat_0[2],
ifelse(df_day_summary$month %in% c(6,7,8),wh_rat_0[3],wh_rat_0[4])))
df_day_summary
## # A tibble: 12
x 7
## month mean_h mean_w cv_h cv_w wh_rat wh_rat_0
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 628. 260. 0.16 0.15 0.414 0.43
## 2 2 366. 157. 0.09 0.12 0.430 0.43
## 3 3 478. 205. 0.1 0.1 0.429 0.42
## 4 4 466. 197. 0.09 0.11 0.422 0.42
## 5 5 445. 203. 0.18 0.11 0.456 0.42
## 6 6 387. 233. 0.09 0.09 0.602 0.59
## 7 7 403. 238. 0.08 0.09 0.591 0.59
## 8 8 437. 258. 0.09 0.09 0.591 0.59
## 9 9 553. 223. 0.08 0.14 0.403 0.4
## 10 10 649. 256. 0.08 0.14 0.395 0.4
## 11 11 732. 287. 0.11 0.13 0.392 0.4
## 12 12 626. 268. 0.08 0.12 0.429 0.43
## month mean_h mean_w cv_h cv_w wh_rat wh_rat_0
## <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1 1 628. 260. 0.16 0.15 0.414 0.43
## 2 2 366. 157. 0.09 0.12 0.430 0.43
## 3 3 478. 205. 0.1 0.1 0.429 0.42
## 4 4 466. 197. 0.09 0.11 0.422 0.42
## 5 5 445. 203. 0.18 0.11 0.456 0.42
## 6 6 387. 233. 0.09 0.09 0.602 0.59
## 7 7 403. 238. 0.08 0.09 0.591 0.59
## 8 8 437. 258. 0.09 0.09 0.591 0.59
## 9 9 553. 223. 0.08 0.14 0.403 0.4
## 10 10 649. 256. 0.08 0.14 0.395 0.4
## 11 11 732. 287. 0.11 0.13 0.392 0.4
## 12 12 626. 268. 0.08 0.12 0.429 0.43
Выведем сравнительный график
data.frame(month=rep((1:12),2),inout=rep(c(0,1),c(12,12)),
wh_rat=c(df_day_summary$wh_rat_0,df_day_summary$wh_rat)) %>%
ggplot(aes(x = as.factor(month), y = wh_rat, fill = as.factor(inout)))+
geom_col(position = "dodge")+
scale_fill_discrete(name="in/out",
labels=c("in 1","out 0"))+
labs(title="Входное / выходное отношение посетителей раб./вых.", x="Месяц", y="раб./вых.")
wh_rat=c(df_day_summary$wh_rat_0,df_day_summary$wh_rat)) %>%
ggplot(aes(x = as.factor(month), y = wh_rat, fill = as.factor(inout)))+
geom_col(position = "dodge")+
scale_fill_discrete(name="in/out",
labels=c("in 1","out 0"))+
labs(title="Входное / выходное отношение посетителей раб./вых.", x="Месяц", y="раб./вых.")
Как видно, значения
близкие. Переходим к одномерной сезонности, месячной и квартальной. По предыдущим примерам видно, что визуализация хорошо помогает при анализе временных
рядов, особенно если это касается присутствия сезонных паттернов. Рассмотрим
визуальный анализ с использование двух пакетов.
Сезонный анализ с пакетом forecast
Пакет forecast содержит
несколько функций для сезонного анализа, основанного на графике пакета ggplot2 и поддерживает
работу с объектами ts. Например функция ggseasonplot выводит сезонные месячные изменения показателя
отдельно по каждому году. Это позволяет оценить, как изменяется сезонность от
года к году. Применим эту функцию для нашего месячного временного ряда
ggseasonplot(ts_month,year.labels=TRUE,continuous=TRUE)
По этому графику хорошо
видно, что временной ряд из года в год имеет повторяемый характер, что говорит
о наличии сезонности. Кроме того видно, что временной ряд имеет падающий тренд.
Другой наглядный способ
представить временной ряд - это график в полярных координатах, 360 градусов
разделяются на сектора (360/частота). Расстояние от полярного центра
представляет величину наблюдения.
ggseasonplot(ts_month, polar = TRUE)
В таком представлении
также четко видна сезонность и падающая тенденция. Спираль улитки с каждым
годом становится ближе к центру.
Еще одна полезная
функция ggsubseriesplot. Показывает, как изменяется значение показателя в определенный месяц от
года к году. Горизонтальные линии показывают средние значения для каждого
месяца. На таком графике видна и сезонность и тренд показателя в рамках
определенного месяца.
ggsubseriesplot(ts_month)
Сезонный анализ с пакетом TSstudio
Пакет TSstudio предоставляет
набор интерактивных функций визуализации данных на основе пакет plotly для сезонного
анализа. Он поддерживает несколько объектов временных рядов, такие как ts, xts, zoo и объекты типа data.frame (data.frame, data.table и tbl). Функция ts_seasonal предоставляет
несколько типов сезонных графиков. Аргумент type устанавливает тип графика, по умолчанию тип
аргумента “normal”, что выводит аналогичный сезонный график как функция ggseasonplot:
ts_seasonal(ts_month,type ="normal")
Представление графика с
типом “cycle”
позволяет отдельно более четко чем в первом случае представить уровень
показателя и градиент его изменения по каждому месяцу
ts_seasonal(ts_month, type = "cycle")
Также очень наглядно и
полезно представление в виде набора двенадцати бохплотов. На таком
представлении виден как уровень, так и вариация показателя по каждому месяцу.
ts_seasonal(ts_month, type = "box")
Тип “all” выводит сразу все три
типа графиков, что тоже может быть полезным для общего представления о
временном ряду
.
ts_seasonal(ts_month, type = "all")
С помощью функции ts_heatmap можно вывести
тепловую карту временного ряда, чем больше затемнение, тем выше значение
показателя
ts_heatmap(ts_month, color = "Reds")
На рассмотренных
примерах мы убедились, что использование описательной статистики и инструментов
визуализации позволяет более глубоко исследовать природу временного ряда, что
безусловно является абсолютно необходимым для последующего качественного
прогнозирования. Также мы отметили, что существует тесная взаимосвязь между
частотой серии и типом сезонных моделей. Временной ряд с более низкой частотой
(например, ежемесячно или ежеквартально) потенциально будет иметь один
доминирующий сезонный характер. С другой стороны, если частота серии выше, то
во временном ряду можно выделить несколько сезонных моделей. Например дневной
временной ряд сочетает в себе две сезонности - по дням недели (рабочий,
выходной) и по сезону (зима, весна, лето, осень). Еще больше сезонностей имеет почасовой
временной ряд, например та же посещаемость магазина. Она зависит от времени
суток, от дня недели и от сезона. При выделении сезонных коэффициентов также нельзя
забывать о присутствии тренда, который может искажать значения сезонности,
поэтому для анализа надо брать данные с исключением тренда.
Комментариев нет:
Отправить комментарий