воскресенье, 31 мая 2020 г.

Временные ряды : сезонность временного ряда


Сезонность является одним из основных компонентов временного ряда. Она играет ключевую роль в прогнозирование таких временных рядов, как посещаемость и выручка розничных магазинов, так как это род вида деятельности носит ярко выраженный сезонный характер. В этой статье мы остановимся на методах и подходах к идентификации и классификации сезонных моделей подобных временных рядов. Рассмотрим использование описательных инструментов статистики, а также рассмотрим методы визуализации.

Типы сезонности

Рассматриваемые нами временные ряды строго упорядочены, то есть всегда повторяются в одном и том же порядке (часы в течение дня, дни недели - с понедельника по воскресенье, месяцы года - с января по декабрь). И так от дня к дню, недели к недели и от года к году. Сезонность существует всегда там, где определенный характер событий связан с определенным периодом во временном ряду. Например посещаемость магазина по часам, по дням недели в зависимости от времени года, по месяцам.
Сезонность можно разделить на две категории :
одномерная сезонность: когда есть только одна доминирующая сезонность, например месячная посещаемость магазинов, она практически не изменяется от года к году
многомерная сезонность : если существует более чем одна доминирующая сезонность, это посещаемость по часам - зависит от сезона и дня недели, посещаемость по дням недели - зависит от сезона (летом - влияние дачного сезона).
Для примеры мы будем продолжать использовать сгенерированный набор данных по посещаемости розничного магазина одежды, он представляет пример с одномерной (квартальный и месячный временной ряд) и многомерной (дневной временной ряд) сезонностями.

Начнем с просмотра общей информации и графиков. 
Дневной временной ряд.

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

ts_plot(ts_day,
        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

ts_plot(ts_month,
        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

ts_plot(ts_qr,
        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>

Добавим к нему еще одну колонку обозначающую рабочий день (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

## # 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

Видно, что отношение среднего количества посетителей рабочий / выходной максимально летом (дачный сезон) и минимально осенью.
Выведем это соотношение графически

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="Посетители")



Кроме того, видно, что посещаемость в рабочие дни более стабильна, это подтверждает величина вариации, выходные 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))

## # A tibble: 1 x 2
##   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

## [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

## # 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

Выведем сравнительный график

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="раб./вых.")



Как видно, значения близкие. Переходим к одномерной сезонности, месячной и квартальной. По предыдущим примерам видно, что визуализация хорошо помогает при анализе временных рядов, особенно если это касается присутствия сезонных паттернов. Рассмотрим визуальный анализ с использование двух пакетов.

Сезонный анализ с пакетом 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")

На рассмотренных примерах мы убедились, что использование описательной статистики и инструментов визуализации позволяет более глубоко исследовать природу временного ряда, что безусловно является абсолютно необходимым для последующего качественного прогнозирования. Также мы отметили, что существует тесная взаимосвязь между частотой серии и типом сезонных моделей. Временной ряд с более низкой частотой (например, ежемесячно или ежеквартально) потенциально будет иметь один доминирующий сезонный характер. С другой стороны, если частота серии выше, то во временном ряду можно выделить несколько сезонных моделей. Например дневной временной ряд сочетает в себе две сезонности - по дням недели (рабочий, выходной) и по сезону (зима, весна, лето, осень). Еще больше сезонностей имеет почасовой временной ряд, например та же посещаемость магазина. Она зависит от времени суток, от дня недели и от сезона. При выделении сезонных коэффициентов также нельзя забывать о присутствии тренда, который может искажать значения сезонности, поэтому для анализа надо брать данные с исключением тренда.



















































































Комментариев нет:

Отправить комментарий