From 3da46503ede97a74639fc05106b0ee1e5729e5ec Mon Sep 17 00:00:00 2001 From: Ben Letham Date: Thu, 3 May 2018 11:10:11 -0700 Subject: [PATCH] Add utility for plotting changepoints (Py), and put in plotting file (R and Py) --- R/NAMESPACE | 4 +- R/R/plot.R | 34 ++++ R/R/utils.R | 33 ---- ...epoints.Rd => add_changepoints_to_plot.Rd} | 10 +- R/man/compile_stan_model.Rd | 2 +- R/man/create_metric_data.Rd | 38 ++-- R/man/get_changepoint_matrix.Rd | 18 -- R/man/get_prophet_stan_model.Rd | 2 +- R/man/make_metrics_function.Rd | 18 ++ R/man/metrics.Rd | 174 +++++++++--------- python/fbprophet/plot.py | 34 ++++ 11 files changed, 206 insertions(+), 161 deletions(-) rename R/man/{layer_changepoints.Rd => add_changepoints_to_plot.Rd} (77%) delete mode 100644 R/man/get_changepoint_matrix.Rd create mode 100644 R/man/make_metrics_function.Rd diff --git a/R/NAMESPACE b/R/NAMESPACE index 86ba8ff..89162c8 100644 --- a/R/NAMESPACE +++ b/R/NAMESPACE @@ -1,20 +1,20 @@ # Generated by roxygen2: do not edit by hand +S3method(plot,prophet) S3method(predict,prophet) +export(add_changepoints_to_plot) export(add_regressor) export(add_seasonality) export(all_metrics) export(cross_validation) export(dyplot.prophet) export(fit.prophet) -export(layer_changepoints) export(mae) export(make_future_dataframe) export(mape) export(me) export(mpe) export(mse) -export(plot.prophet) export(plot_forecast_component) export(predictive_samples) export(prophet) diff --git a/R/R/plot.R b/R/R/plot.R index 1264b2b..1ee71be 100644 --- a/R/R/plot.R +++ b/R/R/plot.R @@ -302,6 +302,40 @@ plot_seasonality <- function(m, name, uncertainty = TRUE) { return(gg.s) } +#' Get layers to overlay significant changepoints on prophet forecast plot. +#' +#' @param m Prophet model object. +#' @param threshold Numeric, changepoints where abs(delta) >= threshold are +#' significant. (Default 0.01) +#' @param cp_color Character, line color. (Default "red") +#' @param cp_linetype Character or integer, line type. (Default "dashed") +#' @param trend Logical, if FALSE, do not draw trend line. (Default TRUE) +#' @param ... Other arguments passed on to layers. +#' +#' @return A list of ggplot2 layers. +#' +#' @examples +#' \dontrun{ +#' plot(m, fcst) + add_changepoints_to_plot(m) +#' } +#' +#' @export +add_changepoints_to_plot <- function(m, threshold = 0.01, cp_color = "red", + cp_linetype = "dashed", trend = TRUE, ...) { + layers <- list() + if (trend) { + trend_layer <- ggplot2::geom_line( + ggplot2::aes_string("ds", "trend"), color = cp_color, ...) + layers <- append(layers, trend_layer) + } + signif_changepoints <- m$changepoints[abs(m$params$delta) >= threshold] + cp_layer <- ggplot2::geom_vline( + xintercept = as.integer(signif_changepoints), color = cp_color, + linetype = cp_linetype, ...) + layers <- append(layers, cp_layer) + return(layers) +} + #' Plot the prophet forecast. #' #' @param x Prophet object. diff --git a/R/R/utils.R b/R/R/utils.R index 2116e91..e69de29 100644 --- a/R/R/utils.R +++ b/R/R/utils.R @@ -1,33 +0,0 @@ -#' Get layers to overlay significant changepoints on prophet forecast plot. -#' -#' @param m Prophet model object. -#' @param threshold Numeric, changepoints where abs(delta) >= threshold are -#' significant. (Default 0.01) -#' @param cp_color Character, line color. (Default "red") -#' @param cp_linetype Character or integer, line type. (Default "dashed") -#' @param trend Logical, if FALSE, do not draw trend line. (Default TRUE) -#' @param ... Other arguments passed on to layers. -#' -#' @return A list of ggplot2 layers. -#' -#' @examples -#' \dontrun{ -#' plot(m, fcst) + layer_changepoints(m) -#' } -#' -#' @export -layer_changepoints <- function(m, threshold = 0.01, cp_color = "red", - cp_linetype = "dashed", trend = TRUE, ...) { - layers <- list() - if (trend) { - trend_layer <- ggplot2::geom_line( - ggplot2::aes_string("ds", "trend"), color = cp_color, ...) - layers <- append(layers, trend_layer) - } - signif_changepoints <- m$changepoints[abs(m$params$delta) >= threshold] - cp_layer <- ggplot2::geom_vline( - xintercept = as.integer(signif_changepoints), color = cp_color, - linetype = cp_linetype, ...) - layers <- append(layers, cp_layer) - return(layers) -} diff --git a/R/man/layer_changepoints.Rd b/R/man/add_changepoints_to_plot.Rd similarity index 77% rename from R/man/layer_changepoints.Rd rename to R/man/add_changepoints_to_plot.Rd index 674c717..96dad1d 100644 --- a/R/man/layer_changepoints.Rd +++ b/R/man/add_changepoints_to_plot.Rd @@ -1,10 +1,10 @@ % Generated by roxygen2: do not edit by hand -% Please edit documentation in R/utils.R -\name{layer_changepoints} -\alias{layer_changepoints} +% Please edit documentation in R/plot.R +\name{add_changepoints_to_plot} +\alias{add_changepoints_to_plot} \title{Get layers to overlay significant changepoints on prophet forecast plot.} \usage{ -layer_changepoints(m, threshold = 0.01, cp_color = "red", +add_changepoints_to_plot(m, threshold = 0.01, cp_color = "red", cp_linetype = "dashed", trend = TRUE, ...) } \arguments{ @@ -29,7 +29,7 @@ Get layers to overlay significant changepoints on prophet forecast plot. } \examples{ \dontrun{ -plot(m, fcst) + layer_changepoints(m) +plot(m, fcst) + add_changepoints_to_plot(m) } } diff --git a/R/man/compile_stan_model.Rd b/R/man/compile_stan_model.Rd index a385c05..ebf9811 100644 --- a/R/man/compile_stan_model.Rd +++ b/R/man/compile_stan_model.Rd @@ -4,7 +4,7 @@ \alias{compile_stan_model} \title{Compile Stan model} \usage{ -compile_stan_model(model) +compile_stan_model() } \arguments{ \item{model}{String 'linear' or 'logistic' to specify a linear or logistic diff --git a/R/man/create_metric_data.Rd b/R/man/create_metric_data.Rd index 6dc5e9d..8242f42 100644 --- a/R/man/create_metric_data.Rd +++ b/R/man/create_metric_data.Rd @@ -1,18 +1,20 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/metrics.R -\name{create_metric_data} -\alias{create_metric_data} -\title{Prepare dataframe for metrics calculation.} -\usage{ -create_metric_data(fcst) -} -\arguments{ -\item{fcst}{Dataframe output of `predict`.} -} -\value{ -A dataframe only with y and yhat as a column. -} -\description{ -Prepare dataframe for metrics calculation. -} -\keyword{internal} +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/metrics.R +\name{create_metric_data} +\alias{create_metric_data} +\title{Prepare dataframe for metrics calculation.} +\usage{ +create_metric_data(m = NULL, df = NULL) +} +\arguments{ +\item{m}{Prophet object. Default NULL} + +\item{df}{A dataframe which is output of `simulated_historical_forecasts` or `cross_validation` Default NULL} +} +\value{ +A dataframe only with y and yhat as a column. +} +\description{ +Prepare dataframe for metrics calculation. +} +\keyword{internal} diff --git a/R/man/get_changepoint_matrix.Rd b/R/man/get_changepoint_matrix.Rd deleted file mode 100644 index 08e87e1..0000000 --- a/R/man/get_changepoint_matrix.Rd +++ /dev/null @@ -1,18 +0,0 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/prophet.R -\name{get_changepoint_matrix} -\alias{get_changepoint_matrix} -\title{Gets changepoint matrix for history dataframe.} -\usage{ -get_changepoint_matrix(m) -} -\arguments{ -\item{m}{Prophet object.} -} -\value{ -array of indexes. -} -\description{ -Gets changepoint matrix for history dataframe. -} -\keyword{internal} diff --git a/R/man/get_prophet_stan_model.Rd b/R/man/get_prophet_stan_model.Rd index 243ad2d..40819d4 100644 --- a/R/man/get_prophet_stan_model.Rd +++ b/R/man/get_prophet_stan_model.Rd @@ -4,7 +4,7 @@ \alias{get_prophet_stan_model} \title{Load compiled Stan model} \usage{ -get_prophet_stan_model(model) +get_prophet_stan_model() } \arguments{ \item{model}{String 'linear' or 'logistic' to specify a linear or logistic diff --git a/R/man/make_metrics_function.Rd b/R/man/make_metrics_function.Rd new file mode 100644 index 0000000..c3df5b3 --- /dev/null +++ b/R/man/make_metrics_function.Rd @@ -0,0 +1,18 @@ +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/metrics.R +\name{make_metrics_function} +\alias{make_metrics_function} +\title{Meta function to make the function which evaluate metrics.} +\usage{ +make_metrics_function(metrics) +} +\arguments{ +\item{metrics}{metrics function} +} +\value{ +A function using for metrics evaluation. +} +\description{ +Meta function to make the function which evaluate metrics. +} +\keyword{internal} diff --git a/R/man/metrics.Rd b/R/man/metrics.Rd index 96d74c2..9f9d081 100644 --- a/R/man/metrics.Rd +++ b/R/man/metrics.Rd @@ -1,83 +1,91 @@ -% Generated by roxygen2: do not edit by hand -% Please edit documentation in R/metrics.R -\name{metrics} -\alias{metrics} -\alias{me} -\alias{mse} -\alias{rmse} -\alias{mae} -\alias{mpe} -\alias{mape} -\alias{all_metrics} -\title{Metrics for Time Series Forecasts} -\usage{ -me(fcst) - -mse(fcst) - -rmse(fcst) - -mae(fcst) - -mpe(fcst) - -mape(fcst) - -all_metrics(fcst) -} -\arguments{ -\item{fcst}{Dataframe output of `predict`.} -} -\value{ -metrics value (numeric) -} -\description{ -A time-series forecast requires making a quantitative prediction of future values. -After forecast, we also have to provide accurracy of forecasts to check wether the forecast serves our need. -Metrics for time series forecasts are so useful in telling you how your model is good and helping you determine which particular forecasting models work best. -} -\details{ -Here, as a notation, we assume that \eqn{y} is the actual value and \eqn{yhat} is the forecast value. - -Mean Error (ME, \code{me}) - -The Mean Error (ME) is defined by the formula: -\deqn{ \frac{1}{n} \sum_{t=1}^{n} y_{t}-yhat_{t} .} - -Mean Squared Error (MSE, \code{mse}) - -The Mean Squared Error (MSE) is defined by the formula: -\deqn{ \frac{1}{n} \sum_{t=1}^{n} (y_{t}-yhat_{t})^2 .} - -Root Mean Square Error (RMSE, \code{rmse}) - -Root Mean Square Error (RMSE) is define by the formula: -\deqn{ \sqrt{\frac{1}{n} \sum_{t=1}^{n} (y_{t}-yhat_{t})^2} .} - -Mean Absolute Error (MAE, \code{mae}) - -The Mean Absolute Error (MAE) is defined by the formula: -\deqn{ \frac{1}{n} \sum_{t=1}^{n} | y_{t}-yhat_{t} | .} - -Mean Percentage Error (MPE, \code{mpe}) - -The Mean Percentage Error (MPE) is usually expressed as a percentage -and is defined by the formula: -\deqn{ \frac{100}{n} \sum_{t=1}^{n} \frac {y_{t}-yhat_{t}}{y_{t}} .} - -Mean Absolute Percentage Error (MAPE, \code{mape}) - -The Mean absolute Percentage Error (MAPE), also known as Mean Absolute Percentage Deviation (MAPD), is usually expressed as a percentage, -and is defined by the formula: -\deqn{ \frac{100}{n} \sum_{t=1}^{n} | \frac {y_{t}-yhat_{t}}{y_{t}}| .} -} -\examples{ -\dontrun{ -# Create example model -library(readr) -df <- read_csv('../tests/testthat/data.csv') -m <- prophet(df) -# You can check your models's accuracy using me, mse, rmse ...etc. -print(rmse(m)) -} -} +% Generated by roxygen2: do not edit by hand +% Please edit documentation in R/metrics.R +\name{metrics} +\alias{metrics} +\alias{me} +\alias{mse} +\alias{rmse} +\alias{mae} +\alias{mpe} +\alias{mape} +\alias{all_metrics} +\title{Metrics for Time Series Forecasts} +\usage{ +me(m = NULL, df = NULL) + +mse(m = NULL, df = NULL) + +rmse(m = NULL, df = NULL) + +mae(m = NULL, df = NULL) + +mpe(m = NULL, df = NULL) + +mape(m = NULL, df = NULL) + +all_metrics(m = NULL, df = NULL) +} +\arguments{ +\item{m}{Prophet object. Default NULL} + +\item{df}{A dataframe which is output of `simulated_historical_forecasts` or `cross_validation` Default NULL} +} +\value{ +metrics value (numeric) +} +\description{ +A time-series forecast requires making a quantitative prediction of future values. +After forecast, we also have to provide accurracy of forecasts to check wether the forecast serves our need. +Metrics for time series forecasts are so useful in telling you how your model is good and helping you determine which particular forecasting models work best. +} +\details{ +Here, as a notation, we assume that \eqn{y} is the actual value and \eqn{yhat} is the forecast value. + +Mean Error (ME, \code{me}) + +The Mean Error (ME) is defined by the formula: +\deqn{ \frac{1}{n} \sum_{t=1}^{n} y_{t}-yhat_{t} .} + +Mean Squared Error (MSE, \code{mse}) + +The Mean Squared Error (MSE) is defined by the formula: +\deqn{ \frac{1}{n} \sum_{t=1}^{n} (y_{t}-yhat_{t})^2 .} + +Root Mean Square Error (RMSE, \code{rmse}) + +Root Mean Square Error (RMSE) is define by the formula: +\deqn{ \sqrt{\frac{1}{n} \sum_{t=1}^{n} (y_{t}-yhat_{t})^2} .} + +Mean Absolute Error (MAE, \code{mae}) + +The Mean Absolute Error (MAE) is defined by the formula: +\deqn{ \frac{1}{n} \sum_{t=1}^{n} | y_{t}-yhat_{t} | .} + +Mean Percentage Error (MPE, \code{mpe}) + +The Mean Percentage Error (MPE) is usually expressed as a percentage +and is defined by the formula: +\deqn{ \frac{100}{n} \sum_{t=1}^{n} \frac {y_{t}-yhat_{t}}{y_{t}} .} + +Mean Absolute Percentage Error (MAPE, \code{mape}) + +The Mean absolute Percentage Error (MAPE), also known as Mean Absolute Percentage Deviation (MAPD), is usually expressed as a percentage, +and is defined by the formula: +\deqn{ \frac{100}{n} \sum_{t=1}^{n} | \frac {y_{t}-yhat_{t}}{y_{t}}| .} +} +\examples{ +\dontrun{ +# Create example model +library(readr) +library(prophet) +df <- read_csv('../tests/testthat/data.csv') +m <- prophet(df) +future <- make_future_dataframe(m, periods = 365) +forecast <- predict(m, future) +all_metrics(forecast) +df.cv <- cross_validation(m, horizon = 100, units = 'days') +all_metrics(df.cv) +# You can check your models's accuracy using me, mse, rmse ...etc. +print(rmse(m)) +} +} diff --git a/python/fbprophet/plot.py b/python/fbprophet/plot.py index 4b8fe61..2b2c6de 100644 --- a/python/fbprophet/plot.py +++ b/python/fbprophet/plot.py @@ -333,3 +333,37 @@ def plot_seasonality(m, name, ax=None, uncertainty=True): ax.set_xlabel('ds') ax.set_ylabel(name) return artists + + +def add_changepoints_to_plot( + ax, m, fcst, threshold=0.01, cp_color='r', cp_linestyle='--', trend=True, +): + """Add markers for significant changepoints to prophet forecast plot. + + Example: + fig = m.plot(forecast) + add_changepoints_to_plot(fig.gca(), m, forecast) + + Parameters + ---------- + ax: axis on which to overlay changepoint markers. + m: Prophet model. + fcst: Forecast output from m.predict. + threshold: Threshold on trend change magnitude for significance. + cp_color: Color of changepoint markers. + cp_linestyle: Linestyle for changepoint markers. + trend: If True, will also overlay the trend. + + Returns + ------- + a list of matplotlib artists + """ + artists = [] + if trend: + artists.append(ax.plot(fcst['ds'], fcst['trend'], c=cp_color)) + signif_changepoints = m.changepoints[ + np.abs(np.nanmean(m.params['delta'], axis=0)) >= threshold + ] + for cp in signif_changepoints: + artists.append(ax.axvline(x=cp, c=cp_color, ls=cp_linestyle)) + return artists