Код для воспроизведения примеров
Все примеры, приведенные в сообщениях из этой серии, можно воспроизвести с помощью кода, который хранится в Github-репозитории ranalytics/intro_to_prophet. Описание используемых в примерах данных представлено в первом сообщении.
Функция add_regressor()
Эта функция имеет следующие аргументы:
- m - модельный объект;
- name - название добавляемого предиктора;
- prior.scale - параметр, используемый для регуляризации эффекта добавляемого предиктора (в этой статье не рассматривается, однако см., например, здесь);
- standardize - позволяет стандартизовать значения добавляемого предиктора перед подгонкой модели (по умолчанию принимает значение "auto" - в этом случае предиктор будет стандартизован, если только он не является индикаторной переменной со значениями 1 и 0; другие возможные значения этого аргумента - TRUE и FALSE);
- mode - необязательный параметр, определяющий характер сезонности добавляемого предиктора (по умолчанию равен m$seasonality.mode).
Все предикторы, добавляемые в модель с помощью функции add_regressor(), должны присутствовать в таблице с обучающими данными. Аналогично, для расчета предсказаний будущие значения каждого предиктора должны присутствовать в таблице с датами, задающими прогнозный отрезок времени. Последнее обстоятельство делает расчет прогноза с использованием количественных предикторов и многих качественных переменных проблематичным: будущие значения таких предикторов (в отличие, например, от дат официальных праздников и других регулярных событий) обычно исследователю не известны. Как правило, для решения этой проблемы сначала строят отдельные модели временных рядов для каждого предиктора, а затем используют предсказанные с помощью таких моделей будущие значения предикторов для получения искомого прогноза. Конечно, такой подход существенно усложняет весь процесс моделирования (поскольку возникает необходимость построения надежных моделей для отдельных предикторов), однако в большинстве случаев это - лучшее, что можно сделать. Именно такой стратегией мы воспользуемся и в описанном ниже примере.
Обратите внимание: собранные с помощью функции tq_get() данные относятся к периоду с 2016-01-01 по 2019-05-26 (включительно), что соответствует периоду обучающих данных (таблица train_df). Важно также понимать, что на момент написания этой статьи (5 октября 2019 г.) интересующий нас прогнозный период (2019-05-27 - 2019-08-24) уже стал историей и, соответственно, цены на акции для этого периода были известны. Но, конечно же, мы не можем использовать эти ставшие историей данные для прогнозирования стоимости биткоина на период с 2019-05-27 по 2019-08-24: в реальной ситуации при расчете прогноза подобное "заглядывание в будущее" было бы невозможно.
Есть одна небольшая проблема с собранными нами данными по цене акций Amazon, Google и Facebook: все три переменные имеют пропущенные значения, что связано с прекращением торгов в выходные дни и во время государственных праздников США.
Хотя Prophet-модели мало чувствительны к наличию пропущенных значений в зависимой переменной (Taylor & Letham 2017), пропущенные значения в предикторах недопустимы. Для восстановления NA-значений в переменных amzn, goog и fb мы воспользуемся простой и хорошо подходящей для этого случая стратегией LOСF ("last observation carried forward"), которая заключается в том, что каждое пропущенное значение заменяется на последнее предшествующее ему непропущенное значение (или на ближайшее следующее за ним непропущенное значение, если ряд начинается с NA, как в нашем случае). Одну из реализаций метода LOCF можно найти в функции na_locf() из пакета imputeTS:
Как отмечено выше, цены на акции Amazon, Google и Facebook выбраны в качестве предикторов исключительно из удобства. Тем не менее, все три переменные демонстрируют определенную корреляцию со стоимостью биткоина и, соответственно, вполне подходят для моделирования:
Однако, прежде чем перейти к построению модели стоимости биткоина, нам необходимо решить проблему с будущими значениями предикторов (см. выше). Для этого мы построим отдельные модели для каждого предиктора, а затем рассчитаем их предсказанные значения для интересующего нас прогнозного периода:
На рис. 4 представлены оцененные компоненты модели. Обратите внимание, что эффекты всех трех предикторов объединены в один компонент (см. "extra_regressors_additive" на графике внизу):
С помощью рассмотренной ранее функции plot_forecast_component() мы можем также изобразить эффекты отдельных предикторов (рис. 5):
Согласно полученной модели, эффект переменной amzn оказался сильнее (примерный диапазон значений от -0.2 до 0.2), чем эффекты goog (от -0.005 до 0.005) и fb (от -0.06 до 0.06). Мы можем сделать такое заключение благодаря тому, что эффекты всех трех предикторов представлены на одной шкале и поэтому сравнимы (значения всех этих переменных перед подгонкой модели были стандартизованы - см. выше описание аргумента standardize функции add_regressor()). Интересно также, что характер изменения эффекта amzn во времени почти зеркально отличается от такового у переменных goog и fb. Я оставлю читателю возможность подумать над интерпретацией этого наблюдения.
Другие статьи из серии:
Добавляем предикторы в модель стоимости биткоина
В качестве примера добавим в модель стоимости биткоина цены на акции трех компаний - Amazon, Google и Facebook. Стоит подчеркнуть, что выбор цен на акции в качестве предикторов, равно как и выбор именно этих компаний, ни с чем не связаны - просто такие данные легко получить из публично доступных источников. Для сбора этих данных воспользуемся пакетом tidyquant:library(tidyquant)
library(prophet)
library(dplyr)
library(ggplot2)
# Собираем данные по цене акций Amazon, Google и Facebook на момент
# закрытия торгов, переименовываем переменные для удобства и
# логарифмируем значения цены:
AMZN <- tq_get(x = "AMZN", get = "stock.prices",
from = "2016-01-01", to = "2019-05-26") %>%
rename(ds = date, amzn = close) %>%
select(ds, amzn) %>%
mutate(amzn = log(amzn))
GOOG <- tq_get(x = "GOOG", get = "stock.prices",
from = "2016-01-01", to = "2019-05-26") %>%
rename(ds = date, goog = close) %>%
select(ds, goog) %>%
mutate(goog = log(goog))
FB <- tq_get(x = "FB", get = "stock.prices",
from = "2016-01-01", to = "2019-05-26") %>%
rename(ds = date, fb = close) %>%
select(ds, fb) %>%
mutate(fb = log(fb))
# Добавляем данные по цене акций в таблицу с обучающими данными:
train_df <- left_join(train_df, AMZN) %>%
left_join(., GOOG) %>%
left_join(FB)
Обратите внимание: собранные с помощью функции tq_get() данные относятся к периоду с 2016-01-01 по 2019-05-26 (включительно), что соответствует периоду обучающих данных (таблица train_df). Важно также понимать, что на момент написания этой статьи (5 октября 2019 г.) интересующий нас прогнозный период (2019-05-27 - 2019-08-24) уже стал историей и, соответственно, цены на акции для этого периода были известны. Но, конечно же, мы не можем использовать эти ставшие историей данные для прогнозирования стоимости биткоина на период с 2019-05-27 по 2019-08-24: в реальной ситуации при расчете прогноза подобное "заглядывание в будущее" было бы невозможно.
Есть одна небольшая проблема с собранными нами данными по цене акций Amazon, Google и Facebook: все три переменные имеют пропущенные значения, что связано с прекращением торгов в выходные дни и во время государственных праздников США.
train_df
# A tibble: 1,242 x 5
y ds amzn goog fb
<dbl> <date> <dbl> <dbl> <dbl>
1 6.07 2016-01-01 NA NA NA
2 6.07 2016-01-02 NA NA NA
3 6.06 2016-01-03 NA NA NA
4 6.07 2016-01-04 6.46 6.61 4.63
5 6.07 2016-01-05 6.45 6.61 4.63
6 6.06 2016-01-06 6.45 6.61 4.63
7 6.13 2016-01-07 6.41 6.59 4.58
8 6.12 2016-01-08 6.41 6.57 4.58
9 6.10 2016-01-09 NA NA NA
10 6.10 2016-01-10 NA NA NA
# ... with 1,232 more rows
Хотя Prophet-модели мало чувствительны к наличию пропущенных значений в зависимой переменной (Taylor & Letham 2017), пропущенные значения в предикторах недопустимы. Для восстановления NA-значений в переменных amzn, goog и fb мы воспользуемся простой и хорошо подходящей для этого случая стратегией LOСF ("last observation carried forward"), которая заключается в том, что каждое пропущенное значение заменяется на последнее предшествующее ему непропущенное значение (или на ближайшее следующее за ним непропущенное значение, если ряд начинается с NA, как в нашем случае). Одну из реализаций метода LOCF можно найти в функции na_locf() из пакета imputeTS:
library(imputeTS)
# Восстанавливаем пропущенные значения цены акций по методу LOCF:
train_df <- train_df %>%
mutate(amzn = na_locf(amzn),
goog = na_locf(goog),
fb = na_locf(fb))
train_df
# A tibble: 1,242 x 5
y ds amzn goog fb
<dbl> <date> <dbl> <dbl> <dbl>
1 6.07 2016-01-01 6.46 6.61 4.63
2 6.07 2016-01-02 6.46 6.61 4.63
3 6.06 2016-01-03 6.46 6.61 4.63
4 6.07 2016-01-04 6.46 6.61 4.63
5 6.07 2016-01-05 6.45 6.61 4.63
6 6.06 2016-01-06 6.45 6.61 4.63
7 6.13 2016-01-07 6.41 6.59 4.58
8 6.12 2016-01-08 6.41 6.57 4.58
9 6.10 2016-01-09 6.41 6.57 4.58
10 6.10 2016-01-10 6.41 6.57 4.58
# ... with 1,232 more rows
Как отмечено выше, цены на акции Amazon, Google и Facebook выбраны в качестве предикторов исключительно из удобства. Тем не менее, все три переменные демонстрируют определенную корреляцию со стоимостью биткоина и, соответственно, вполне подходят для моделирования:
Рис. 1 |
Однако, прежде чем перейти к построению модели стоимости биткоина, нам необходимо решить проблему с будущими значениями предикторов (см. выше). Для этого мы построим отдельные модели для каждого предиктора, а затем рассчитаем их предсказанные значения для интересующего нас прогнозного периода:
# Строим отдельные модели для каждого предиктора (параметры моделей
# подобраны после предварительного экспериментирования):
m_amzn <- prophet(rename(AMZN, y = amzn),
n.changepoints = 15,
changepoint.range = 0.9,
weekly.seasonality = FALSE)
m_goog <- prophet(rename(GOOG, y = goog),
n.changepoints = 15,
changepoint.range = 0.9,
weekly.seasonality = FALSE)
m_fb <- prophet(rename(FB, y = fb),
n.changepoints = 15,
changepoint.range = 0.9,
weekly.seasonality = FALSE)
# Рассчитываем прогнозные значения для цен на акции:
future_df_shares <- future_df %>% select(ds)
amzn_forecast <- predict(m_amzn, future_df_shares)
goog_forecast <- predict(m_goog, future_df_shares)
fb_forecast <- predict(m_fb, future_df_shares)
future_df_shares <- future_df_shares %>%
mutate(amzn = amzn_forecast$yhat,
goog = goog_forecast$yhat,
fb = fb_forecast$yhat)
Полученные модели и соответствующие прогнозные значения цен на акции выглядят так:
Теперь у нас есть все, чтобы построить модель стоимости биткоина с тремя предикторами и рассчитать прогнозные значения:
Рис. 2 |
# Модель стоимости биткоина, включающая три предиктора:
M16 <- prophet(n.changepoints = 15, changepoint.range = 0.9)
M16 <- add_regressor(M16, 'amzn')
M16 <- add_regressor(M16, 'goog')
M16 <- add_regressor(M16, 'fb')
M16 <- fit.prophet(M16, train_df)
forecast_M16 <- predict(M16, future_df_shares)
# Визуализация полученной модели и прогнозных значений:
plot(M16, forecast_M16)
Рис. 3 |
# Визуализация компонентов модели:
prophet_plot_components(M16, forecast_M16)
Рис. 4 |
library(gridExtra)
amzn_comp <- plot_forecast_component(M16, forecast_M16, name = "amzn") +
ggtitle("Amazon")
goog_comp <- plot_forecast_component(M16, forecast_M16, name = "goog") +
ggtitle("Google")
fb_comp <- plot_forecast_component(M16, forecast_M16, name = "fb") +
ggtitle("Facebook")
grid.arrange(amzn_comp, goog_comp, fb_comp, ncol = 3)
Рис. 5 |
Как насчет качественных предикторов?
В рассмотренном нами примере все добавленные в модель дополнительные переменные были количественными. В случае с качественными предикторами у исследователя есть два варианта на выбор: либо представить значения таких переменных в виде "праздников", либо воспользоваться описанной выше более общей функцией add_regressor(). В обоих случаях качественные переменные необходимо преобразовать в индикаторные (т.е. создать отдельные столбцы для каждого уровня переменной со значениями 1/0).
***
В следующем сообщении из этой серии мы рассмотрим способ выбора оптимальной модели временного ряда с использованием перекрестной проверки.Другие статьи из серии:
Но вот такой вопрос: Прогнозируется не вещественная переменная, а вероятность какого-то события. Например, ежедневно фиксировались частота покупок определенного товара из покупателей некой фокус-группы и нужно оценить динамику вероятности продаж на какой-то период. Есть ли какие-то принципиальные трудности замены отклика - натуральной переменной на частоты произошедших событий при использовании функций пакета? Всего наилучшего.
Как таковой возможности моделировать пропорции в Prophet, к сожалению, нету. Но возможны следующие подходы.
1. Если очень хочется использовать Prophet, то:
1a: во избежание возможных ситуаций, когда предсказания будут бессмысленными (например, отрицательные значения), можно попробовать моделировать не исходные пропорции, а их логарифм (с последующим экспоненцированием предсказанных значений для перехода на оригинальную шкалу)
1b: построить две модели - одну для количества единиц интересующего товара, а вторую - для общего количества всех остальных товаров. Далее на основе полученных прогнозов по каждой модели можно было бы рассчитать долю для интересующего товара на каждую дату прогнозного периода. Я бы пошел именно этим путем, поскольку он дал бы бизнесу более полную картину: и возможные величины продаж в абсолютном выражении, и долю интересующего товара.
2. Воспользоваться другими подходами, в частности GLARMA (https://cran.r-project.org/web/packages/glarma/vignettes/glarma.pdf)
Отправить комментарий