Использование пакета fb-prophet для прогнозирования по группе с дополнительными регрессорами в R

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

library(data.table)
library(prophet)
library(dplyr)

# one year of months to be used for generating predictions
ds = c('2016-01-01', '2016-02-01','2016-03-01','2016-04-01','2016-05-01','2016-06-01','2016-07-01','2016-08-01','2016-09-01','2016-10-01','2016-11-01','2016-12-01' )

# historical customer counts
y = c (78498,12356,93732,5556,410,10296,9779,744,16407,100484,23954,141398,10575,850,16334,17496,1643,28074,93181,
       18770,129968,11590,850,16738,17510,1376,27931,94369,18444,134850,13386,919,19075,18050,1565,31296,112094,27995,
       167094,13402,1422,22766,20072,2340,37863,87346,16180,119863,7691,725,16931,12163,1241,25872,87455,16322,116390,
       6994,620,13524,11059,990,22188,105473,23652,154145,13520,1008,18857,19209,1632,31105,102252,21284,138779,11670,
       918,16078,16679,1257,26755,115033,22415,139835,13965,936,18027,18642,1407,28622,155371,40556,174321,25119,1859,
       35326,28844,2962,51582,108817,19158,109864,8693,756,14358,13390,1091,21419)

# the segment channels of the customers
segment_channel = c('Existing_Omni', 'Existing_Retail', 'Existing_Direct', 'NTB_Omni', 'NTB_Retail', 'NTB_Direct', 'React_Omni', 'React_Retail', 'React_Direct')

# an external regressor to be added to the model (in my data there are like 40 of these regressor variables that I would like too add)
flash_sale = c(1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
               2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3)

fake_data = merge(ds,segment_channel, all.y=TRUE)
setnames(fake_data, 'x', 'ds')
setnames(fake_data, 'y', 'segment_channel')
nrow(fake_data) # should be 108 rows, the 9 customer segements for each of the months in 2016

# next join the known customer counts, let's say we have them for the first 8 months of the year

fake_data = cbind(fake_data, y)
fake_data = cbind(fake_data, flash_sale)

# set some of the y values to NA so we can pretend we are trying to predict them using the ds time series as well as the flash sale values,
# which will be known in advance

fake_data = as.data.table(fake_data)
fake_data$ds = as.Date(fake_data$ds)
fake_data[, y := ifelse(ds >= '2016-08-01', NA, y)]

Этот код сгенерирует набор данных, довольно похожий на тот, с которым я работаю для своей проблемы, поэтому, надеюсь, вы сможете воспроизвести то, что я делаю. По сути, есть две вещи, которые я хотел бы делать с этими данными. Первый довольно прост, я хочу иметь возможность явно добавить регрессор (например, flash_sale в этом примере в созданную мной модель пророка. Я могу сделать это довольно легко следующим образом:

christ <- tibble(
  holiday = 'christ',
  ds = as.Date(c('2016-11-01', '2017-11-01', '2018-11-01',
                 '2019-11-01')),
  lower_window = 0,
  upper_window = 1
)

nye <- tibble(
  holiday = 'nye',
  ds = as.Date(c('2016-11-01', '2017-12-01', '2018-11-01',
                 '2019-11-01')),
  lower_window = 0,
  upper_window = 1
)

holidays <- bind_rows(nye, christ)

m <- prophet(holidays = holidays)
m<- add_regressor(m, name = "flash_sale")
m <- fit.prophet(m, fake_data)
forecast <- predict(m, fake_data)


prophet_plot_components(m, forecast)

Это должно создать довольно уродливый график, но довольно легко увидеть, что, учитывая данные, это должно помочь, и я мог бы добавить несколько строк для добавления дополнительных регрессоров. Итак, пока у нас все в порядке. Но другая проблема в том, что у меня есть 9 сегментных каналов, с которыми я работаю, и я не хочу строить отдельную модель для каждого из них. К счастью, я нашел довольно хорошую ссылку на переполнение стека, которая выполняет предсказание сгруппированного пророка: Использование пакета Prophet для прогнозирования по группам в Dataframe в R

fcst = fake_data %>%  
  group_by(segment_channel) %>%
  do(predict(prophet(., seasonality.mode = 'multiplicative', holidays = holidays, seasonality.prior.scale = 10, changepoint.prior.scale = .034), make_future_dataframe(prophet(.), periods = 11, freq='month'))) %>% 
  dplyr::select(ds, segment_channel, yhat)

fcst
> fcst
# A tibble: 207 x 3
# Groups:   segment_channel [9]
   ds                  segment_channel   yhat
   <dttm>              <fct>            <dbl>
 1 2016-01-01 00:00:00 Existing_Direct 38712.
 2 2016-02-01 00:00:00 Existing_Direct 40321.
 3 2016-03-01 00:00:00 Existing_Direct 42648.
 4 2016-04-01 00:00:00 Existing_Direct 45130.
 5 2016-05-01 00:00:00 Existing_Direct 46580.
 6 2016-06-01 00:00:00 Existing_Direct 49437.
 7 2016-07-01 00:00:00 Existing_Direct 50651.
 8 2016-08-01 00:00:00 Existing_Direct 52685.
 9 2016-09-01 00:00:00 Existing_Direct 54719.
10 2016-10-01 00:00:00 Existing_Direct 56687.
# ... with 197 more rows

Это примерно то, что я хочу! Прохладный. Итак, теперь все, что мне нужно сделать, это выяснить, как получить мои сгруппированные прогнозы и мои регрессоры за один шаг. Я знаю, что внутри do могут быть многострочные операторы, поэтому вот что я пробовал, чтобы заставить это работать:

> fcst = fake_data %>%  
+   group_by(segment_channel) %>%
+   do(
+     predict(prophet(., seasonality.mode = 'multiplicative', holidays = holidays, seasonality.prior.scale = 10, changepoint.prior.scale = .034), 
+     add_regressor(prophet(., holidays = holidays), name = 'flash_sale'),
+     fit.prophet(prophet(. , holidays = holidays)),
+     make_future_dataframe(prophet(.), periods = 11, freq='month'))) %>% 
+   dplyr::select(ds, segment_channel, yhat)
Disabling yearly seasonality. Run prophet with yearly.seasonality=TRUE to override this.
Disabling weekly seasonality. Run prophet with weekly.seasonality=TRUE to override this.
Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
n.changepoints greater than number of observations. Using 4
Disabling yearly seasonality. Run prophet with yearly.seasonality=TRUE to override this.
Disabling weekly seasonality. Run prophet with weekly.seasonality=TRUE to override this.
Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.
n.changepoints greater than number of observations. Using 4
Error in add_regressor(prophet(., holidays = holidays), name = "flash_sale") : 
  Regressors must be added prior to model fitting.

Штопать. Похоже, он работал, но что-то в том, как я пытался добавить регрессор, не было кошерным. Затем я попробовал вот так:

> fcst = fake_data %>%  
+   group_by(segment_channel) %>%
+   do(
+     prophet(holidays = holidays),
+     add_regressor(prophet(., holidays = holidays), name = 'flash_sale'),
+     fit.prophet(prophet(. , holidays = holidays)),
+     predict(prophet(., seasonality.mode = 'multiplicative', holidays = holidays, seasonality.prior.scale = 10, changepoint.prior.scale = .034),
+     make_future_dataframe(prophet(.), periods = 11, freq='month'))) %>% 
+   dplyr::select(ds, segment_channel, yhat)
Error: Can only supply one unnamed argument, not 4
Call `rlang::last_error()` to see a backtrace
> fcst = fake_data %>%  
+   group_by(segment_channel) %>%
+   do(
+     add_regressor(prophet(., holidays = holidays), name = 'flash_sale'),
+     fit.prophet(prophet(. , holidays = holidays)),
+     predict(prophet(., seasonality.mode = 'multiplicative', holidays = holidays, seasonality.prior.scale = 10, changepoint.prior.scale = .034),
+     make_future_dataframe(prophet(.), periods = 11, freq='month'))) %>% 
+   dplyr::select(ds, segment_channel, yhat)
Error: Can only supply one unnamed argument, not 3
Call `rlang::last_error()` to see a backtrace

Я очень запутался на этом этапе, поэтому я просто надеюсь, что что-то в сети может знать правильное заклинание, которое мне нужно, чтобы добраться туда, куда я собираюсь.


person jane_thompson    schedule 08.05.2019    source источник