business-science / modeltime

Modeltime unlocks time series forecast models and machine learning in one framework
https://business-science.github.io/modeltime/
Other
532 stars 82 forks source link

Prophet :: Logistic Growth - Modeltime Support with logistic_cap & logistic_floor #25

Closed Steviey closed 4 years ago

Steviey commented 4 years ago

Ubuntu: 16.4 LTS, R: 4.0.2, modeltime: 0.0.2

If param growth is set to logistic, param: 'cap' has to be set e.g.:

https://facebook.github.io/prophet/docs/saturating_forecasts.html

It seems this can't be done either in "set_engine()" nor in "prophet_reg()".

Message & Traceback: `Error in setup_dataframe(m, history, initialize_scales = TRUE) : Capacities must be supplied for logistic growth. █

  1. ├─global::bAlgos(i, f, "testing") ~/R/daScript.R:32006:12
  2. │ └─global::standAloneAlgos(...) ~/R/daScript.R:31621:12
  3. │ └─%>%(...) ~/R/daScript.R:16790:20
  4. │ ├─base::withVisible(eval(quote(_fseq(_lhs)), env, env))
  5. │ └─base::eval(quote(_fseq(_lhs)), env, env)
  6. │ └─base::eval(quote(_fseq(_lhs)), env, env)
  7. │ └─_fseq(_lhs)
  8. │ └─magrittr::freduce(value, _function_list)
  9. │ ├─base::withVisible(function_list[k])
    1. │ └─function_list[k]
    2. │ ├─parsnip::fit(...)
    3. │ └─parsnip::fit.model_spec(...)
    4. │ └─parsnip:::form_xy(...)
    5. │ └─parsnip:::xy_xy(...)
    6. │ ├─base::system.time(...)
    7. │ └─parsnip:::eval_mod(...)
    8. │ └─rlang::eval_tidy(e, ...)
    9. ├─modeltime::prophet_fit_impl(...)
    10. │ └─prophet::fit.prophet(m, df)
    11. │ └─prophet:::setup_dataframe(m, history, initialize_scales = TRUE)
    12. │ └─base::stop("Capacities must be supplied for logistic growth.")
    13. └─(function () ...
    14. └─lobstr::cst() ~/R/daScript.R:8:25` Thank you.
mdancho84 commented 4 years ago

Hi @Steviey

I need to look into this more to see it I can make it work with a column called "cap" like prophet does it.

It's worth noting that you can accomplish something similar with a "constrained interval" transformation.

Steviey commented 4 years ago

Oki

mdancho84 commented 4 years ago

This is now complete. I've added support for prophet_reg() and prophet_boost(). Here's how to perform logistic growth:

Modeltime Implementation

library(tidymodels)
library(modeltime)
library(tidyverse)
library(lubridate)
library(timetk)

# Data
m750 <- m4_monthly %>% 
    filter(id == "M750")

# Prophet

model_fit_prophet <- prophet_reg(
    growth = "logistic",
    logistic_cap = 11000
) %>%
    set_engine(engine = "prophet") %>%
    fit(value ~ date, m750)
#> Disabling weekly seasonality. Run prophet with weekly.seasonality=TRUE to override this.
#> Disabling daily seasonality. Run prophet with daily.seasonality=TRUE to override this.

model_fit_prophet
#> parsnip model object
#> 
#> Fit time:  861ms 
#> PROPHET Model
#> - growth: 'logistic'
#> - n.changepoints: 25
#> - changepoint.range: 0.8
#> - yearly.seasonality: 'auto'
#> - weekly.seasonality: 'auto'
#> - daily.seasonality: 'auto'
#> - seasonality.mode: 'additive'
#> - changepoint.prior.scale: 0.05
#> - seasonality.prior.scale: 10
#> - holidays.prior.scale: 10
#> - logistic_cap: 11000
#> - logistic_floor: NULL
#> - extra_regressors: 0

# Prophet Boost

model_fit_prophet_boost <- prophet_boost(
    growth = "logistic",
    logistic_cap = 11000,
    seasonality_yearly = FALSE,
    seasonality_weekly = FALSE,
    seasonality_daily  = FALSE
) %>%
    set_engine(engine = "prophet_xgboost") %>%
    fit(value ~ date 
        + as.numeric(date)
        + month(date, label = TRUE) 
        + fourier_vec(date, period = 12), 
        data = m750)

model_fit_prophet_boost
#> parsnip model object
#> 
#> Fit time:  390ms 
#> PROPHET w/ XGBoost Errors
#> ---
#> Model 1: PROPHET
#>  - growth: 'logistic'
#>  - n.changepoints: 25
#>  - changepoint.range: 0.8
#>  - yearly.seasonality: 'FALSE'
#>  - weekly.seasonality: 'FALSE'
#>  - daily.seasonality: 'FALSE'
#>  - seasonality.mode: 'additive'
#>  - changepoint.prior.scale: 0.05
#>  - seasonality.prior.scale: 10
#>  - holidays.prior.scale: 10
#>  - logistic_cap: 11000
#>  - logistic_floor: NULL
#> 
#> ---
#> Model 2: XGBoost Errors
#> 
#> xgboost::xgb.train(params = list(eta = 0.3, max_depth = 6, gamma = 0, 
#>     colsample_bytree = 1, min_child_weight = 1, subsample = 1), 
#>     data = x, nrounds = 15, watchlist = wlist, verbose = 0, objective = "reg:squarederror", 
#>     nthread = 1)

# Forecast

modeltime_table(
    model_fit_prophet,
    model_fit_prophet_boost
) %>%
    modeltime_forecast(
        h = 12 * 10,
        actual_data = m750
    ) %>%
    plot_modeltime_forecast(
        .conf_interval_show = FALSE,
        .interactive        = FALSE
    )


# In-Sample "Fitted" Predictions

modeltime_table(
    model_fit_prophet,
    model_fit_prophet_boost
) %>%
    modeltime_forecast(
        new_data = m750,
        actual_data = m750
    ) %>%
    plot_modeltime_forecast(
        .conf_interval_show = FALSE,
        .interactive        = FALSE
    )

Created on 2020-08-26 by the reprex package (v0.3.0)