From 40afef26b459f93763279ad44edae86207c1f319 Mon Sep 17 00:00:00 2001 From: "Sean J. Taylor" Date: Tue, 13 Nov 2018 10:17:59 -0800 Subject: [PATCH] Generate documentation sub-menus --- docs/_docs/contributing.md | 7 ++- docs/_docs/diagnostics.md | 14 +++++ docs/_docs/installation.md | 9 +++ docs/_docs/multiplicative_seasonality.md | 10 +++ docs/_docs/non-daily_data.md | 28 +++++++++ docs/_docs/outliers.md | 7 +++ docs/_docs/quick_start.md | 35 +++++++++++ docs/_docs/saturating_forecasts.md | 25 ++++++++ ...nality,_holiday_effects,_and_regressors.md | 63 ++++++++++++++++++- docs/_docs/trend_changepoints.md | 24 +++++++ docs/_docs/uncertainty_intervals.md | 26 ++++++++ .../nav/collection_nav_group_item.html | 9 +++ docs/_sass/_react_docs_nav.scss | 26 +++++++- docs/nbconvert_template.tpl | 19 +++++- 14 files changed, 296 insertions(+), 6 deletions(-) diff --git a/docs/_docs/contributing.md b/docs/_docs/contributing.md index dedf7e5..9bfa11a 100644 --- a/docs/_docs/contributing.md +++ b/docs/_docs/contributing.md @@ -3,6 +3,9 @@ layout: docs docid: "contributing" title: "Getting Help and Contributing" permalink: /docs/contributing.html +subsections: + - id: documentation + title: Generating documentation --- Prophet has a non-fixed release cycle but we will be making bugfixes in response to user feedback and adding features. Its current state is Beta (v0.3), we expect no obvious bugs. Please let us know if you encounter a bug by [filing an issue](https://github.com/facebook/prophet/issues). Github issues is also the right place to ask questions about using Prophet. @@ -13,7 +16,9 @@ If you plan to contribute new features or extensions to the core, please first o The R and Python versions are kept feature identical, but new features can be implemented for each method in separate commits. -## Documentation + + +## Generating documentation Most of the `doc` pages are generated from [Jupyter notebooks](http://jupyter.org/) in the [notebooks](https://github.com/facebook/prophet/tree/master/notebooks) directory at the base of the source tree. Please make changes there and then rebuild the docs: diff --git a/docs/_docs/diagnostics.md b/docs/_docs/diagnostics.md index e1199e6..e883119 100644 --- a/docs/_docs/diagnostics.md +++ b/docs/_docs/diagnostics.md @@ -3,21 +3,30 @@ layout: docs docid: "diagnostics" title: "Diagnostics" permalink: /docs/diagnostics.html +subsections: --- Prophet includes functionality for time series cross validation to measure forecast error using historical data. This is done by selecting cutoff points in the history, and for each of them fitting the model using data only up to that cutoff point. We can then compare the forecasted values to the actual values. This figure illustrates a simulated historical forecast on the Peyton Manning dataset, where the model was fit to a initial history of 5 years, and a forecast was made on a one year horizon. + ![png](/prophet/static/diagnostics_files/diagnostics_3_0.png) [The Prophet paper](https://peerj.com/preprints/3190.pdf) gives further description of simulated historical forecasts. + + This cross validation procedure can be done automatically for a range of historical cutoffs using the `cross_validation` function. We specify the forecast horizon (`horizon`), and then optionally the size of the initial training period (`initial`) and the spacing between cutoff dates (`period`). By default, the initial training period is set to three times the horizon, and cutoffs are made every half a horizon. + + The output of `cross_validation` is a dataframe with the true values `y` and the out-of-sample forecast values `yhat`, at each simulated forecast date and for each cutoff date. In particular, a forecast is made for every observed point between `cutoff` and `cutoff + horizon`. This dataframe can then be used to compute error measures of `yhat` vs. `y`. + + Here we do cross-validation to assess prediction performance on a horizon of 365 days, starting with 730 days of training data in the first cutoff and then making predictions every 180 days. On this 8 year time series, this corresponds to 11 total forecasts. + ```R # R df.cv <- cross_validation(m, initial = 730, period = 180, horizon = 365, units = 'days') @@ -112,6 +121,7 @@ df_cv.head() The `performance_metrics` utility can be used to compute some useful statistics of the prediction performance (`yhat`, `yhat_lower`, and `yhat_upper` compared to `y`), as a function of the distance from the cutoff (how far into the future the prediction was). The statistics computed are mean squared error (MSE), root mean squared error (RMSE), mean absolute error (MAE), mean absolute percent error (MAPE), and coverage of the `yhat_lower` and `yhat_upper` estimates. These are computed on a rolling window of the predictions in `df_cv` after sorting by horizon (`ds` minus `cutoff`). By default 10% of the predictions will be included in each window, but this can be changed with the `rolling_window` argument. + ```R # R df.p <- performance_metrics(df.cv) @@ -206,6 +216,7 @@ df_p.head() Cross validation performance metrics can be visualized with `plot_cross_validation_metric`, here shown for MAPE. Dots show the absolute percent error for each prediction in `df_cv`. The blue line shows the MAPE, where the mean is taken over a rolling window of the dots. We see for this forecast that errors around 5% are typical for predictions one month into the future, and that errors increase up to around 11% for predictions that are a year out. + ```R # R plot_cross_validation_metric(df.cv, metric = 'mape') @@ -221,4 +232,7 @@ fig = plot_cross_validation_metric(df_cv, metric='mape') The size of the rolling window in the figure can be changed with the optional argument `rolling_window`, which specifies the proportion of forecasts to use in each rolling window. The default is 0.1, corresponding to 10% of rows from `df_cv` included in each window; increasing this will lead to a smoother average curve in the figure. + + The `initial` period should be long enough to capture all of the components of the model, in particular seasonalities and extra regressors: at least a year for yearly seasonality, at least a week for weekly seasonality, etc. + diff --git a/docs/_docs/installation.md b/docs/_docs/installation.md index 7bd8634..c546219 100644 --- a/docs/_docs/installation.md +++ b/docs/_docs/installation.md @@ -3,10 +3,17 @@ layout: docs docid: "installation" title: "Installation" permalink: /docs/installation.html +subsections: + - id: r + title: Using R + - id: python + title: Using Python --- Prophet has two implementations: [R](#installation-in-r) and [Python](#installation-in-python). Note the slight name difference for the Python package. + + ## Installation in R Prophet is a [CRAN package](https://cran.r-project.org/package=prophet) so you can use `install.packages`: @@ -24,6 +31,8 @@ On Windows, R requires a compiler so you'll need to [follow the instructions](ht If you have custom Stan compiler settings, install from source rather than the CRAN binary. + + ## Installation in Python Prophet is on PyPI, so you can use pip to install it: diff --git a/docs/_docs/multiplicative_seasonality.md b/docs/_docs/multiplicative_seasonality.md index b09515d..047dce2 100644 --- a/docs/_docs/multiplicative_seasonality.md +++ b/docs/_docs/multiplicative_seasonality.md @@ -3,9 +3,11 @@ layout: docs docid: "multiplicative_seasonality" title: "Multiplicative Seasonality" permalink: /docs/multiplicative_seasonality.html +subsections: --- By default Prophet fits additive seasonalities, meaning the effect of the seasonality is added to the trend to get the forecast. This time series of the number of air passengers is an example of when additive seasonality does not work: + ```R # R df <- read.csv('../examples/example_air_passengers.csv') @@ -29,8 +31,11 @@ fig = m.plot(forecast) This time series has a clear yearly cycle, but the seasonality in the forecast is too large at the start of the time series and too small at the end. In this time series, the seasonality is not a constant additive factor as assumed by Prophet, rather it grows with the trend. This is multiplicative seasonality. + + Prophet can model multiplicative seasonality by setting `seasonality_mode='multiplicative'` in the input arguments: + ```R # R m <- prophet(df, seasonality.mode = 'multiplicative') @@ -50,6 +55,7 @@ fig = m.plot(forecast) The components figure will now show the seasonality as a percent of the trend: + ```R # R prophet_plot_components(m, forecast) @@ -64,8 +70,11 @@ fig = m.plot_components(forecast) With `seasonality_mode='multiplicative'`, holiday effects will also be modeled as multiplicative. Any added seasonalities or extra regressors will by default use whatever `seasonality_mode` is set to, but can be overriden by specifying `mode='additive'` or `mode='multiplicative'` as an argument when adding the seasonality or regressor. + + For example, this block sets the built-in seasonalities to multiplicative, but includes an additive quarterly seasonality and an additive regressor: + ```R # R m <- prophet(seasonality.mode = 'multiplicative') @@ -79,3 +88,4 @@ m.add_seasonality('quarterly', period=91.25, fourier_order=8, mode='additive') m.add_regressor('regressor', mode='additive') ``` Additive and multiplicative extra regressors will show up in separate panels on the components plot. + diff --git a/docs/_docs/non-daily_data.md b/docs/_docs/non-daily_data.md index 5afa331..9d1ffb3 100644 --- a/docs/_docs/non-daily_data.md +++ b/docs/_docs/non-daily_data.md @@ -3,11 +3,23 @@ layout: docs docid: "non-daily_data" title: "Non-Daily Data" permalink: /docs/non-daily_data.html +subsections: + - title: Sub-daily data + id: sub-daily-data + - title: Data with regular gaps + id: data-with-regular-gaps + - title: Monthly data + id: monthly-data --- + + ## Sub-daily data + + Prophet can make forecasts for time series with sub-daily observations by passing in a dataframe with timestamps in the `ds` column. The format of the timestamps should be YYYY-MM-DD HH:MM:SS - see the example csv [here](https://github.com/facebook/prophet/blob/master/examples/example_yosemite_temps.csv). When sub-daily data are used, daily seasonality will automatically be fit. Here we fit Prophet to data with 5-minute resolution (daily temperatures at Yosemite): + ```R # R df <- read.csv('../examples/example_yosemite_temps.csv') @@ -30,6 +42,7 @@ fig = m.plot(fcst) The daily seasonality will show up in the components plot: + ```R # R prophet_plot_components(m, fcst) @@ -42,10 +55,15 @@ fig = m.plot_components(fcst) ![png](/prophet/static/non-daily_data_files/non-daily_data_7_0.png) + + ## Data with regular gaps + + Suppose the dataset above only had observations from 12a to 6a: + ```R # R df2 <- df %>% @@ -72,6 +90,7 @@ fig = m.plot(fcst) The forecast seems quite poor, with much larger fluctuations in the future than were seen in the history. The issue here is that we have fit a daily cycle to a time series that only has data for part of the day (12a to 6a). The daily seasonality is thus unconstrained for the remainder of the day and is not estimated well. The solution is to only make predictions for the time windows for which there are historical data. Here, that means to limit the `future` dataframe to have times from 12a to 6a: + ```R # R future2 <- future %>% @@ -92,10 +111,17 @@ fig = m.plot(fcst) The same principle applies to other datasets with regular gaps in the data. For example, if the history contains only weekdays, then predictions should only be made for weekdays since the weekly seasonality will not be well estimated for the weekends. + + + + ## Monthly data + + You can use Prophet to fit monthly data. However, the underlying model is continuous-time, which means that you can get strange results if you fit the model to monthly data and then ask for daily forecasts. Here we forecast US retail sales volume for the next 10 years: + ```R # R df <- read.csv('../examples/example_retail_sales.csv') @@ -118,6 +144,7 @@ fig = m.plot(fcst) This is the same issue from above where the dataset has regular gaps. When we fit the yearly seasonality, it only has data for the first of each month and the seasonality components for the remaining days are unidentifiable and overfit. This can be clearly seen by doing MCMC to see uncertainty in the seasonality: + ```R # R m <- prophet(df, seasonality.mode = 'multiplicative', mcmc.samples = 300) @@ -136,6 +163,7 @@ fig = m.plot_components(fcst) The seasonality has low uncertainty at the start of each month where there are data points, but has very high posterior variance in between. When fitting Prophet to monthly data, only make monthly forecasts, which can be done by passing the frequency into `make_future_dataframe`: + ```R # R future <- make_future_dataframe(m, periods = 120, freq = 'month') diff --git a/docs/_docs/outliers.md b/docs/_docs/outliers.md index d3a3d65..5e84faa 100644 --- a/docs/_docs/outliers.md +++ b/docs/_docs/outliers.md @@ -3,9 +3,11 @@ layout: docs docid: "outliers" title: "Outliers" permalink: /docs/outliers.html +subsections: --- There are two main ways that outliers can affect Prophet forecasts. Here we make a forecast on the logged Wikipedia visits to the R page from before, but with a block of bad data: + ```R # R df <- read.csv('../examples/example_wp_log_R_outliers1.csv') @@ -29,8 +31,11 @@ fig = m.plot(forecast) The trend forecast seems reasonable, but the uncertainty intervals seem way too wide. Prophet is able to handle the outliers in the history, but only by fitting them with trend changes. The uncertainty model then expects future trend changes of similar magnitude. + + The best way to handle outliers is to remove them - Prophet has no problem with missing data. If you set their values to `NA` in the history but leave the dates in `future`, then Prophet will give you a prediction for their values. + ```R # R outliers <- (as.Date(df$ds) > as.Date('2010-01-01') @@ -52,6 +57,7 @@ fig = model.plot(model.predict(future)) In the above example the outliers messed up the uncertainty estimation but did not impact the main forecast `yhat`. This isn't always the case, as in this example with added outliers: + ```R # R df <- read.csv('../examples/example_wp_log_R_outliers2.csv') @@ -75,6 +81,7 @@ fig = m.plot(forecast) Here a group of extreme outliers in June 2015 mess up the seasonality estimate, so their effect reverberates into the future forever. Again the right approach is to remove them: + ```R # R outliers <- (as.Date(df$ds) > as.Date('2015-06-01') diff --git a/docs/_docs/quick_start.md b/docs/_docs/quick_start.md index c447538..af285a8 100644 --- a/docs/_docs/quick_start.md +++ b/docs/_docs/quick_start.md @@ -3,17 +3,32 @@ layout: docs docid: "quick_start" title: "Quick Start" permalink: /docs/quick_start.html +subsections: + - title: Python API + id: python-api + - title: R API + id: r-api --- + + ## Python API + + Prophet follows the `sklearn` model API. We create an instance of the `Prophet` class and then call its `fit` and `predict` methods. + The input to Prophet is always a dataframe with two columns: `ds` and `y`. The `ds` (datestamp) column should be of a format expected by Pandas, ideally YYYY-MM-DD for a date or YYYY-MM-DD HH:MM:SS for a timestamp. The `y` column must be numeric, and represents the measurement we wish to forecast. + + As an example, let's look at a time series of the log daily page views for the Wikipedia page for [Peyton Manning](https://en.wikipedia.org/wiki/Peyton_Manning). We scraped this data using the [Wikipediatrend](https://cran.r-project.org/web/packages/wikipediatrend/vignettes/using-wikipediatrend.html) package in R. Peyton Manning provides a nice example because it illustrates some of Prophet's features, like multiple seasonality, changing growth rates, and the ability to model special days (such as Manning's playoff and superbowl appearances). The CSV is available [here](https://github.com/facebook/prophet/blob/master/examples/example_wp_log_peyton_manning.csv). + + First we'll import the data: + ```python # Python import pandas as pd @@ -83,6 +98,7 @@ df.head() We fit the model by instantiating a new `Prophet` object. Any settings to the forecasting procedure are passed into the constructor. Then you call its `fit` method and pass in the historical dataframe. Fitting should take 1-5 seconds. + ```python # Python m = Prophet() @@ -90,6 +106,7 @@ m.fit(df) ``` Predictions are then made on a dataframe with a column `ds` containing the dates for which a prediction is to be made. You can get a suitable dataframe that extends into the future a specified number of days using the helper method `Prophet.make_future_dataframe`. By default it will also include the dates from the history, so we will see the model fit as well. + ```python # Python future = m.make_future_dataframe(periods=365) @@ -148,6 +165,7 @@ future.tail() The `predict` method will assign each row in `future` a predicted value which it names `yhat`. If you pass in historical dates, it will provide an in-sample fit. The `forecast` object here is a new dataframe that includes a column `yhat` with the forecast, as well as columns for components and uncertainty intervals. + ```python # Python forecast = m.predict(future) @@ -224,6 +242,7 @@ forecast[['ds', 'yhat', 'yhat_lower', 'yhat_upper']].tail() You can plot the forecast by calling the `Prophet.plot` method and passing in your forecast dataframe. + ```python # Python fig1 = m.plot(forecast) @@ -234,6 +253,7 @@ fig1 = m.plot(forecast) If you want to see the forecast components, you can use the `Prophet.plot_components` method. By default you'll see the trend, yearly seasonality, and weekly seasonality of the time series. If you include holidays, you'll see those here, too. + ```python # Python fig2 = m.plot_components(forecast) @@ -244,28 +264,37 @@ fig2 = m.plot_components(forecast) More details about the options available for each method are available in the docstrings, for example, via `help(Prophet)` or `help(Prophet.fit)`. The [R reference manual](https://cran.r-project.org/web/packages/prophet/prophet.pdf) on CRAN provides a concise list of all of the available functions, each of which has a Python equivalent. + + + ## R API + + In R, we use the normal model fitting API. We provide a `prophet` function that performs fitting and returns a model object. You can then call `predict` and `plot` on this model object. + ```R # R library(prophet) ``` First we read in the data and create the outcome variable. As in the Python API, this is a dataframe with columns `ds` and `y`, containing the date and numeric value respectively. The ds column should be YYYY-MM-DD for a date, or YYYY-MM-DD HH:MM:SS for a timestamp. As above, we use here the log number of views to Peyton Manning's Wikipedia page, available [here](https://github.com/facebook/prophet/blob/master/examples/example_wp_log_peyton_manning.csv). + ```R # R df <- read.csv('../examples/example_wp_log_peyton_manning.csv') ``` We call the `prophet` function to fit the model. The first argument is the historical dataframe. Additional arguments control how Prophet fits the data and are described in later pages of this documentation. + ```R # R m <- prophet(df) ``` Predictions are made on a dataframe with a column `ds` containing the dates for which predictions are to be made. The `make_future_dataframe` function takes the model object and a number of periods to forecast and produces a suitable dataframe. By default it will also include the historical dates so we can evaluate in-sample fit. + ```R # R future <- make_future_dataframe(m, periods = 365) @@ -284,6 +313,7 @@ tail(future) As with most modeling procedures in R, we use the generic `predict` function to get our forecast. The `forecast` object is a dataframe with a column `yhat` containing the forecast. It has additional columns for uncertainty intervals and seasonal components. + ```R # R forecast <- predict(m, future) @@ -302,6 +332,7 @@ tail(forecast[c('ds', 'yhat', 'yhat_lower', 'yhat_upper')]) You can use the generic `plot` function to plot the forecast, by passing in the model and the forecast dataframe. + ```R # R plot(m, forecast) @@ -312,6 +343,7 @@ plot(m, forecast) You can use the `prophet_plot_components` function to see the forecast broken down into trend, weekly seasonality, and yearly seasonality. + ```R # R prophet_plot_components(m, forecast) @@ -322,4 +354,7 @@ prophet_plot_components(m, forecast) An interactive plot of the forecast using Dygraphs can be made with the command `dyplot.prophet(m, forecast)`. + + More details about the options available for each method are available in the docstrings, for example, via `?prophet` or `?fit.prophet`. This documentation is also available in the [reference manual](https://cran.r-project.org/web/packages/prophet/prophet.pdf) on CRAN. + diff --git a/docs/_docs/saturating_forecasts.md b/docs/_docs/saturating_forecasts.md index 0243c64..123b072 100644 --- a/docs/_docs/saturating_forecasts.md +++ b/docs/_docs/saturating_forecasts.md @@ -3,13 +3,25 @@ layout: docs docid: "saturating_forecasts" title: "Saturating Forecasts" permalink: /docs/saturating_forecasts.html +subsections: + - title: Forecasting Growth + id: forecasting-growth + - title: Saturating Minimum + id: saturating-minimum --- + + ### Forecasting Growth + + By default, Prophet uses a linear model for its forecast. When forecasting growth, there is usually some maximum achievable point: total market size, total population size, etc. This is called the carrying capacity, and the forecast should saturate at this point. + + Prophet allows you to make forecasts using a [logistic growth](https://en.wikipedia.org/wiki/Logistic_function) trend model, with a specified carrying capacity. We illustrate this with the log number of page visits to the [R (programming language)](https://en.wikipedia.org/wiki/R_%28programming_language%29) page on Wikipedia: + ```R # R df <- read.csv('../examples/example_wp_log_R.csv') @@ -20,6 +32,7 @@ df = pd.read_csv('../examples/example_wp_log_R.csv') ``` We must specify the carrying capacity in a column `cap`. Here we will assume a particular value, but this would usually be set using data or expertise about the market size. + ```R # R df$cap <- 8.5 @@ -30,8 +43,11 @@ df['cap'] = 8.5 ``` The important things to note are that `cap` must be specified for every row in the dataframe, and that it does not have to be constant. If the market size is growing, then `cap` can be an increasing sequence. + + We then fit the model as before, except pass in an additional argument to specify logistic growth: + ```R # R m <- prophet(df, growth = 'logistic') @@ -43,6 +59,7 @@ m.fit(df) ``` We make a dataframe for future predictions as before, except we must also specify the capacity in the future. Here we keep capacity constant at the same value as in the history, and forecast 3 years into the future: + ```R # R future <- make_future_dataframe(m, periods = 1826) @@ -63,10 +80,17 @@ fig = m.plot(fcst) The logistic function has an implicit minimum of 0, and will saturate at 0 the same way that it saturates at the capacity. It is possible to also specify a different saturating minimum. + + + + ### Saturating Minimum + + The logistic growth model can also handle a saturating minimum, which is specified with a column `floor` in the same way as the `cap` column specifies the maximum: + ```R # R df$y <- 10 - df$y @@ -95,3 +119,4 @@ fig = m.plot(fcst) To use a logistic growth trend with a saturating minimum, a maximum capacity must also be specified. + diff --git a/docs/_docs/seasonality,_holiday_effects,_and_regressors.md b/docs/_docs/seasonality,_holiday_effects,_and_regressors.md index 461149d..fa5d786 100644 --- a/docs/_docs/seasonality,_holiday_effects,_and_regressors.md +++ b/docs/_docs/seasonality,_holiday_effects,_and_regressors.md @@ -3,14 +3,33 @@ layout: docs docid: "seasonality,_holiday_effects,_and_regressors" title: "Seasonality, Holiday Effects, And Regressors" permalink: /docs/seasonality,_holiday_effects,_and_regressors.html +subsections: + - title: Modeling Holidays and Special Events + id: modeling-holidays-and-special-events + - title: Fourier Order for Seasonalities + id: fourier-order-for-seasonalities + - title: Specifying Custom Seasonalities + id: specifying-custom-seasonalities + - title: Prior scale for holidays and seasonality + id: prior-scale-for-holidays-and-seasonality + - title: Additional regressors + id: additional-regressors --- + + ### Modeling Holidays and Special Events + If you have holidays or other recurring events that you'd like to model, you must create a dataframe for them. It has two columns (`holiday` and `ds`) and a row for each occurrence of the holiday. It must include all occurrences of the holiday, both in the past (back as far as the historical data go) and in the future (out as far as the forecast is being made). If they won't repeat in the future, Prophet will model them and then not include them in the forecast. + + You can also include columns `lower_window` and `upper_window` which extend the holiday out to `[lower_window, upper_window]` days around the date. For instance, if you wanted to included Christmas Eve in addition to Christmas you'd include `lower_window=-1,upper_window=0`. If you wanted to use Black Friday in addition to Thanksgiving, you'd include `lower_window=0,upper_window=1`. You can also include a column `prior_scale` to set the prior scale separately for each holiday, as described below. + + Here we create a dataframe that includes the dates of all of Peyton Manning's playoff appearances: + ```R # R library(dplyr) @@ -52,10 +71,13 @@ superbowls = pd.DataFrame({ }) holidays = pd.concat((playoffs, superbowls)) ``` -Above we have include the superbowl days as both playoff games and superbowl games. This means that the superbowl effect will be an additional additive bonus on top of the playoff effect. +Above we have included the superbowl days as both playoff games and superbowl games. This means that the superbowl effect will be an additional additive bonus on top of the playoff effect. + + Once the table is created, holiday effects are included in the forecast by passing them in with the `holidays` argument. Here we do it with the Peyton Manning data from the Quickstart: + ```R # R m <- prophet(df, holidays = holidays) @@ -68,6 +90,7 @@ forecast = m.fit(df).predict(future) ``` The holiday effect can be seen in the `forecast` dataframe: + ```R # R forecast %>% @@ -175,6 +198,7 @@ forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][ The holiday effects will also show up in the components plot, where we see that there is a spike on the days around playoff appearances, with an especially large spike for the superbowl: + ```R # R prophet_plot_components(m, forecast) @@ -189,10 +213,16 @@ fig = m.plot_components(forecast) Individual holidays can be plotted using the `plot_forecast_component` function (imported from `fbprophet.plot` in Python) like `plot_forecast_component(forecast, 'superbowl')` to plot just the superbowl holiday component. + + + ### Fourier Order for Seasonalities + + Seasonalities are estimated using a partial Fourier sum. See [the paper](https://peerj.com/preprints/3190/) for complete details, and [this figure on Wikipedia](https://en.wikipedia.org/wiki/Fourier_series#/media/File:Fourier_Series.svg) for an illustration of how a partial Fourier sum can approximate an aribtrary periodic signal. The number of terms in the partial sum (the order) is a parameter that determines how quickly the seasonality can change. To illustrate this, consider the Peyton Manning data from the Quickstart. The default Fourier order for yearly seasonality is 10, which produces this fit: + ```R # R m <- prophet(df) @@ -210,6 +240,7 @@ a = plot_yearly(m) The default values are often appropriate, but they can be increased when the seasonality needs to fit higher-frequency changes, and generally be less smooth. The Fourier order can be specified for each built-in seasonality when instantiating the model, here it is increased to 20: + ```R # R m <- prophet(df, yearly.seasonality = 20) @@ -227,14 +258,25 @@ a = plot_yearly(m) Increasing the number of Fourier terms allows the seasonality to fit faster changing cycles, but can also lead to overfitting: N Fourier terms corresponds to 2N variables used for modeling the cycle + + + + ### Specifying Custom Seasonalities + + Prophet will by default fit weekly and yearly seasonalities, if the time series is more than two cycles long. It will also fit daily seasonality for a sub-daily time series. You can add other seasonalities (monthly, quarterly, hourly) using the `add_seasonality` method (Python) or function (R). + + The inputs to this function are a name, the period of the seasonality in days, and the Fourier order for the seasonality. For reference, by default Prophet uses a Fourier order of 3 for weekly seasonality and 10 for yearly seasonality. An optional input to `add_seasonality` is the prior scale for that seasonal component - this is discussed below. + + As an example, here we fit the Peyton Manning data from the Quickstart, but replace the weekly seasonality with monthly seasonality. The monthly seasonality then will appear in the components plot: + ```R # R m <- prophet(weekly.seasonality=FALSE) @@ -254,9 +296,13 @@ fig = m.plot_components(forecast) ![png](/prophet/static/seasonality,_holiday_effects,_and_regressors_files/seasonality,_holiday_effects,_and_regressors_23_0.png) + + ### Prior scale for holidays and seasonality + If you find that the holidays are overfitting, you can adjust their prior scale to smooth them using the parameter `holidays_prior_scale`. By default this parameter is 10, which provides very little regularization. Reducing this parameter dampens holiday effects: + ```R # R m <- prophet(df, holidays = holidays, holidays.prior.scale = 0.05) @@ -368,8 +414,11 @@ forecast[(forecast['playoff'] + forecast['superbowl']).abs() > 0][ The magnitude of the holiday effect has been reduced compared to before, especially for superbowls, which had the fewest observations. There is a parameter `seasonality_prior_scale` which similarly adjusts the extent to which the seasonality model will fit the data. + + Prior scales can be set separately for individual holidays by including a column `prior_scale` in the holidays dataframe. Prior scales for individual seasonalities can be passed as an argument to `add_seasonality`. For instance, the prior scale for just weekly seasonality can be set using: + ```R # R m <- prophet() @@ -383,9 +432,14 @@ m.add_seasonality( name='weekly', period=7, fourier_order=3, prior_scale=0.1) ``` + + + ### Additional regressors + Additional regressors can be added to the linear part of the model using the `add_regressor` method or function. A column with the regressor value will need to be present in both the fitting and prediction dataframes. For example, we can add an additional effect on Sundays during the NFL season. On the components plot, this effect will show up in the 'extra_regressors' plot: + ```R # R nfl_sunday <- function(ds) { @@ -429,8 +483,15 @@ fig = m.plot_components(forecast) NFL Sundays could also have been handled using the "holidays" interface described above, by creating a list of past and future NFL Sundays. The `add_regressor` function provides a more general interface for defining extra linear regressors, and in particular does not require that the regressor be a binary indicator. Another time series could be used as a regressor, although its future values would have to be known. + + The `add_regressor` function has optional arguments for specifying the prior scale (holiday prior scale is used by default) and whether or not the regressor is standardized - see the docstring with `help(Prophet.add_regressor)` in Python and `?add_regressor` in R. Note that regressors must be added prior to model fitting. + + The extra regressor must be known for both the history and for future dates. It thus must either be something that has known future values (such as `nfl_sunday`), or something that has separately been forecasted elsewhere. Prophet will also raise an error if the regressor is constant throughout the history, since there is nothing to fit from it. + + Extra regressors are put in the linear component of the model, so the underlying model is that the time series depends on the extra regressor as either an additive or multiplicative factor (see the next section for multiplicativity). + diff --git a/docs/_docs/trend_changepoints.md b/docs/_docs/trend_changepoints.md index e255a98..fe1953c 100644 --- a/docs/_docs/trend_changepoints.md +++ b/docs/_docs/trend_changepoints.md @@ -3,24 +3,38 @@ layout: docs docid: "trend_changepoints" title: "Trend Changepoints" permalink: /docs/trend_changepoints.html +subsections: + - title: Automatic changepoint detection in Prophet + id: automatic-changepoint-detection-in-prophet + - title: Adjusting trend flexibility + id: adjusting-trend-flexibility + - title: Specifying the locations of the changepoints + id: specifying-the-locations-of-the-changepoints --- You may have noticed in the earlier examples in this documentation that real time series frequently have abrupt changes in their trajectories. By default, Prophet will automatically detect these changepoints and will allow the trend to adapt appropriately. However, if you wish to have finer control over this process (e.g., Prophet missed a rate change, or is overfitting rate changes in the history), then there are several input arguments you can use. + + + ### Automatic changepoint detection in Prophet + Prophet detects changepoints by first specifying a large number of *potential changepoints* at which the rate is allowed to change. It then puts a sparse prior on the magnitudes of the rate changes (equivalent to L1 regularization) - this essentially means that Prophet has a large number of *possible* places where the rate can change, but will use as few of them as possible. Consider the Peyton Manning forecast from the Quickstart. By default, Prophet specifies 25 potential changepoints which are uniformly placed in the first 80% of the time series. The vertical lines in this figure indicate where the potential changepoints were placed: + ![png](/prophet/static/trend_changepoints_files/trend_changepoints_4_0.png) Even though we have a lot of places where the rate can possibly change, because of the sparse prior, most of these changepoints go unused. We can see this by plotting the magnitude of the rate change at each changepoint: + ![png](/prophet/static/trend_changepoints_files/trend_changepoints_6_0.png) The number of potential changepoints can be set using the argument `n_changepoints`, but this is better tuned by adjusting the regularization. The locations of the signification changepoints can be visualized with: + ```R # R plot(m, forecast) + add_changepoints_to_plot(m) @@ -37,9 +51,14 @@ a = add_changepoints_to_plot(fig.gca(), m, forecast) By default changepoints are only inferred for the first 80% of the time series in order to have plenty of runway for projecting the trend forward and to avoid overfitting fluctuations at the end of the time series. This default works in many situations but not all, and can be change using the `changepoint_range` argument. For example, `m = Prophet(changepoint_range=0.9)` in Python or `m <- prophet(changepoint.range = 0.9)` in R will place potential changepoints in the first 90% of the time series. + + + ### Adjusting trend flexibility + If the trend changes are being overfit (too much flexibility) or underfit (not enough flexibility), you can adjust the strength of the sparse prior using the input argument `changepoint_prior_scale`. By default, this parameter is set to 0.05. Increasing it will make the trend *more* flexible: + ```R # R m <- prophet(df, changepoint.prior.scale = 0.5) @@ -58,6 +77,7 @@ fig = m.plot(forecast) Decreasing it will make the trend *less* flexible: + ```R # R m <- prophet(df, changepoint.prior.scale = 0.001) @@ -74,10 +94,14 @@ fig = m.plot(forecast) ![png](/prophet/static/trend_changepoints_files/trend_changepoints_16_0.png) + + ### Specifying the locations of the changepoints + If you wish, rather than using automatic changepoint detection you can manually specify the locations of potential changepoints with the `changepoints` argument. Slope changes will then be allowed only at these points, with the same sparse regularization as before. One could, for instance, create a grid of points as is done automatically, but then augment that grid with some specific dates that are known to be likely to have changes. As another example, the changepoints could be entirely limited to a small set of dates, as is done here: + ```R # R m <- prophet(df, changepoints = c('2014-01-01')) diff --git a/docs/_docs/uncertainty_intervals.md b/docs/_docs/uncertainty_intervals.md index d10a6b2..bb7037a 100644 --- a/docs/_docs/uncertainty_intervals.md +++ b/docs/_docs/uncertainty_intervals.md @@ -3,18 +3,35 @@ layout: docs docid: "uncertainty_intervals" title: "Uncertainty Intervals" permalink: /docs/uncertainty_intervals.html +subsections: + - title: Uncertainty in the trend + id: uncertainty-in-the-trend + - title: Uncertainty in seasonality + id: uncertainty-in-seasonality --- By default Prophet will return uncertainty intervals for the forecast `yhat`. There are several important assumptions behind these uncertainty intervals. + + There are three sources of uncertainty in the forecast: uncertainty in the trend, uncertainty in the seasonality estimates, and additional observation noise. + + + + ### Uncertainty in the trend + The biggest source of uncertainty in the forecast is the potential for future trend changes. The time series we have seen already in this documentation show clear trend changes in the history. Prophet is able to detect and fit these, but what trend changes should we expect moving forward? It's impossible to know for sure, so we do the most reasonable thing we can, and we assume that the *future will see similar trend changes as the history*. In particular, we assume that the average frequency and magnitude of trend changes in the future will be the same as that which we observe in the history. We project these trend changes forward and by computing their distribution we obtain uncertainty intervals. + + One property of this way of measuring uncertainty is that allowing higher flexibility in the rate, by increasing `changepoint_prior_scale`, will increase the forecast uncertainty. This is because if we model more rate changes in the history then we will expect more in the future, and makes the uncertainty intervals a useful indicator of overfitting. + + The width of the uncertainty intervals (by default 80%) can be set using the parameter `interval_width`: + ```R # R m <- prophet(df, interval.width = 0.95) @@ -26,9 +43,15 @@ forecast = Prophet(interval_width=0.95).fit(df).predict(future) ``` Again, these intervals assume that the future will see the same frequency and magnitude of rate changes as the past. This assumption is probably not true, so you should not expect to get accurate coverage on these uncertainty intervals. + + + + ### Uncertainty in seasonality + By default Prophet will only return uncertainty in the trend and observation noise. To get uncertainty in seasonality, you must do full Bayesian sampling. This is done using the parameter `mcmc.samples` (which defaults to 0). We do this here for the first six months of the Peyton Manning data from the Quickstart: + ```R # R m <- prophet(df, mcmc.samples = 300) @@ -41,6 +64,7 @@ forecast = m.fit(df).predict(future) ``` This replaces the typical MAP estimation with MCMC sampling, and can take much longer depending on how many observations there are - expect several minutes instead of several seconds. If you do full sampling, then you will see the uncertainty in seasonal components when you plot them: + ```R # R prophet_plot_components(m, forecast) @@ -55,4 +79,6 @@ fig = m.plot_components(forecast) You can access the raw posterior predictive samples in Python using the method `m.predictive_samples(future)`, or in R using the function `predictive_samples(m, future)`. + There are upstream issues in PyStan for Windows which make MCMC sampling extremely slow. The best choice for MCMC sampling in Windows is to use R, or Python in a Linux VM. + diff --git a/docs/_includes/nav/collection_nav_group_item.html b/docs/_includes/nav/collection_nav_group_item.html index 64896e2..ac017a4 100644 --- a/docs/_includes/nav/collection_nav_group_item.html +++ b/docs/_includes/nav/collection_nav_group_item.html @@ -2,4 +2,13 @@ {{ groupitem.title }} + {% if groupitem.subsections %} + + {% endif %} diff --git a/docs/_sass/_react_docs_nav.scss b/docs/_sass/_react_docs_nav.scss index f0a651e..738233a 100644 --- a/docs/_sass/_react_docs_nav.scss +++ b/docs/_sass/_react_docs_nav.scss @@ -166,6 +166,11 @@ nav.toc { display: block; padding-bottom: 10px; padding-top: 10px; + + .navListSubItems { + padding-bottom: 0px; + padding-top: 0px; + } } h3 { @@ -181,7 +186,7 @@ nav.toc { ul { padding-left: 0; - padding-right: 24px; + padding-right: 18px; li { list-style-type: none; @@ -194,8 +199,8 @@ nav.toc { display: inline-block; font-size: 14px; line-height: 1.1em; - margin: 2px 10px 5px; - padding: 5px 0 2px; + margin: 2px 10px 2px; + padding: 1px 0 1px; transition: color 0.3s; &:hover, @@ -207,6 +212,20 @@ nav.toc { color: $primary-bg; font-weight: 900; } + + &.navItem { + font-weight: bold; + } + + } + + .navListSubItem { + padding-top: 0; + margin-left: 20px; + + a.navItem { + font-weight: normal; + } } } } @@ -329,4 +348,5 @@ nav.toc { padding: 0 10px; } } + } \ No newline at end of file diff --git a/docs/nbconvert_template.tpl b/docs/nbconvert_template.tpl index 9e68fed..4c1370c 100644 --- a/docs/nbconvert_template.tpl +++ b/docs/nbconvert_template.tpl @@ -6,6 +6,13 @@ layout: docs docid: "{{resources['metadata']['name']}}" title: "{{resources['metadata']['name'].replace('_', ' ').title()}}" permalink: /docs/{{resources['metadata']['name']}}.html +subsections: +{%- for cell in nb['cells'] if cell.cell_type == 'markdown' and '##' in cell.source -%} +{% for line in cell.source.split('\n') if line.startswith('##') %} + - title: {{ line.lstrip('# ') }} + id: {{ line.lstrip('# ').lower().replace(' ', '-') }} +{%- endfor -%} +{% endfor %} --- {%- endblock header -%} @@ -43,4 +50,14 @@ permalink: /docs/{{resources['metadata']['name']}}.html {% block data_png %} ![png](/prophet/static/{{ output.metadata.filenames['image/png'] }}) -{% endblock data_png %} \ No newline at end of file +{% endblock data_png %} + +{% block markdowncell %} +{%- set lines = cell.source.split('\n') -%} +{%- for line in lines -%} +{% if line.startswith('##') %} + +{% endif %} +{{ line }} +{% endfor %} +{% endblock markdowncell %} \ No newline at end of file