|
@@ -6,7 +6,7 @@
|
|
|
## of patent rights can be found in the PATENTS file in the same directory.
|
|
|
|
|
|
## Makes R CMD CHECK happy due to dplyr syntax below
|
|
|
-utils::globalVariables(c(
|
|
|
+globalVariables(c(
|
|
|
"ds", "y", "cap", ".",
|
|
|
"component", "dow", "doy", "holiday", "holidays", "holidays_lower", "holidays_upper", "ix",
|
|
|
"lower", "n", "stat", "trend", "row_number", "extra_regressors", "col",
|
|
@@ -190,21 +190,21 @@ validate_column_name <- function(
|
|
|
'trend', 'seasonal', 'seasonalities', 'daily', 'weekly', 'yearly',
|
|
|
'holidays', 'zeros', 'extra_regressors', 'yhat'
|
|
|
)
|
|
|
- rn_l = paste0(reserved_names,"_lower")
|
|
|
- rn_u = paste0(reserved_names,"_upper")
|
|
|
+ rn_l = paste(reserved_names,"_lower",sep="")
|
|
|
+ rn_u = paste(reserved_names,"_upper",sep="")
|
|
|
reserved_names = c(reserved_names, rn_l, rn_u,
|
|
|
c("ds", "y", "cap", "floor", "y_scaled", "cap_scaled"))
|
|
|
if(name %in% reserved_names){
|
|
|
stop("Name ", name, " is reserved.")
|
|
|
}
|
|
|
- if(check_holidays && !is.null(m$holidays) &&
|
|
|
+ if(check_holidays & !is.null(m$holidays) &
|
|
|
(name %in% unique(m$holidays$holiday))){
|
|
|
stop("Name ", name, " already used for a holiday.")
|
|
|
}
|
|
|
- if(check_seasonalities && (!is.null(m$seasonalities[[name]]))){
|
|
|
+ if(check_seasonalities & (!is.null(m$seasonalities[[name]]))){
|
|
|
stop("Name ", name, " already used for a seasonality.")
|
|
|
}
|
|
|
- if(check_regressors && (!is.null(m$seasonalities[[name]]))){
|
|
|
+ if(check_regressors & (!is.null(m$seasonalities[[name]]))){
|
|
|
stop("Name ", name, " already used for an added regressor.")
|
|
|
}
|
|
|
}
|
|
@@ -274,8 +274,11 @@ set_date <- function(ds = NULL, tz = "GMT") {
|
|
|
ds <- as.character(ds)
|
|
|
}
|
|
|
|
|
|
- fmt <- if (min(nchar(ds)) < 12) "%Y-%m-%d" else "%Y-%m-%d %H:%M:%S"
|
|
|
- ds <- as.POSIXct(ds, format = fmt, tz = tz)
|
|
|
+ if (min(nchar(ds)) < 12) {
|
|
|
+ ds <- as.POSIXct(ds, format = "%Y-%m-%d", tz = tz)
|
|
|
+ } else {
|
|
|
+ ds <- as.POSIXct(ds, format = "%Y-%m-%d %H:%M:%S", tz = tz)
|
|
|
+ }
|
|
|
attr(ds, "tzone") <- tz
|
|
|
return(ds)
|
|
|
}
|
|
@@ -459,7 +462,7 @@ set_changepoints <- function(m) {
|
|
|
m$changepoints.t <- sort(
|
|
|
time_diff(m$changepoints, m$start, "secs")) / m$t.scale
|
|
|
} else {
|
|
|
- m$changepoints.t <- 0 # dummy changepoint
|
|
|
+ m$changepoints.t <- c(0) # dummy changepoint
|
|
|
}
|
|
|
return(m)
|
|
|
}
|
|
@@ -473,7 +476,7 @@ set_changepoints <- function(m) {
|
|
|
#' @keywords internal
|
|
|
get_changepoint_matrix <- function(m) {
|
|
|
A <- matrix(0, nrow(m$history), length(m$changepoints.t))
|
|
|
- for (i in seq_along(m$changepoints.t)) {
|
|
|
+ for (i in 1:length(m$changepoints.t)) {
|
|
|
A[m$history$t >= m$changepoints.t[i], i] <- 1
|
|
|
}
|
|
|
return(A)
|
|
@@ -491,7 +494,7 @@ get_changepoint_matrix <- function(m) {
|
|
|
fourier_series <- function(dates, period, series.order) {
|
|
|
t <- time_diff(dates, set_date('1970-01-01 00:00:00'))
|
|
|
features <- matrix(0, length(t), 2 * series.order)
|
|
|
- for (i in seq_len(series.order)) {
|
|
|
+ for (i in 1:series.order) {
|
|
|
x <- as.numeric(2 * i * pi * t / period)
|
|
|
features[, i * 2 - 1] <- sin(x)
|
|
|
features[, i * 2] <- cos(x)
|
|
@@ -511,7 +514,7 @@ fourier_series <- function(dates, period, series.order) {
|
|
|
#' @keywords internal
|
|
|
make_seasonality_features <- function(dates, period, series.order, prefix) {
|
|
|
features <- fourier_series(dates, period, series.order)
|
|
|
- colnames(features) <- paste(prefix, seq_len(ncol(features)), sep = '_delim_')
|
|
|
+ colnames(features) <- paste(prefix, 1:ncol(features), sep = '_delim_')
|
|
|
return(data.frame(features))
|
|
|
}
|
|
|
|
|
@@ -538,13 +541,13 @@ make_holiday_features <- function(m, dates) {
|
|
|
&& !is.na(.$upper_window)) {
|
|
|
offsets <- seq(.$lower_window, .$upper_window)
|
|
|
} else {
|
|
|
- offsets <- 0
|
|
|
+ offsets <- c(0)
|
|
|
}
|
|
|
names <- paste(.$holiday, '_delim_', ifelse(offsets < 0, '-', '+'),
|
|
|
abs(offsets), sep = '')
|
|
|
dplyr::data_frame(ds = .$ds + offsets * 24 * 3600, holiday = names)
|
|
|
}) %>%
|
|
|
- dplyr::mutate(x = 1) %>%
|
|
|
+ dplyr::mutate(x = 1.) %>%
|
|
|
tidyr::spread(holiday, x, fill = 0)
|
|
|
|
|
|
holiday.features <- data.frame(ds = set_date(dates)) %>%
|
|
@@ -682,7 +685,7 @@ add_seasonality <- function(m, name, period, fourier.order, prior.scale = NULL)
|
|
|
#'
|
|
|
#' @keywords internal
|
|
|
make_all_seasonality_features <- function(m, df) {
|
|
|
- seasonal.features <- data.frame(row.names = seq_len(nrow(df)))
|
|
|
+ seasonal.features <- data.frame(row.names = 1:nrow(df))
|
|
|
prior.scales <- c()
|
|
|
|
|
|
# Seasonality features
|
|
@@ -710,7 +713,7 @@ make_all_seasonality_features <- function(m, df) {
|
|
|
|
|
|
if (ncol(seasonal.features) == 0) {
|
|
|
seasonal.features <- data.frame(zeros = rep(0, nrow(df)))
|
|
|
- prior.scales <- 1
|
|
|
+ prior.scales <- c(1.)
|
|
|
}
|
|
|
return(list(seasonal.features = seasonal.features,
|
|
|
prior.scales = prior.scales))
|
|
@@ -1034,7 +1037,9 @@ predict.prophet <- function(object, df = NULL, ...) {
|
|
|
cols <- c(cols, 'floor')
|
|
|
}
|
|
|
df <- df[cols]
|
|
|
- df <- dplyr::bind_cols(df, seasonal.components, intervals)
|
|
|
+ df <- df %>%
|
|
|
+ dplyr::bind_cols(seasonal.components) %>%
|
|
|
+ dplyr::bind_cols(intervals)
|
|
|
df$yhat <- df$trend + df$seasonal
|
|
|
return(df)
|
|
|
}
|
|
@@ -1056,7 +1061,7 @@ piecewise_linear <- function(t, deltas, k, m, changepoint.ts) {
|
|
|
# Get cumulative slope and intercept at each t
|
|
|
k_t <- rep(k, length(t))
|
|
|
m_t <- rep(m, length(t))
|
|
|
- for (s in seq_along(changepoint.ts)) {
|
|
|
+ for (s in 1:length(changepoint.ts)) {
|
|
|
indx <- t >= changepoint.ts[s]
|
|
|
k_t[indx] <- k_t[indx] + deltas[s]
|
|
|
m_t[indx] <- m_t[indx] + gammas[s]
|
|
@@ -1081,14 +1086,14 @@ piecewise_logistic <- function(t, cap, deltas, k, m, changepoint.ts) {
|
|
|
# Compute offset changes
|
|
|
k.cum <- c(k, cumsum(deltas) + k)
|
|
|
gammas <- rep(0, length(changepoint.ts))
|
|
|
- for (i in seq_along(changepoint.ts)) {
|
|
|
+ for (i in 1:length(changepoint.ts)) {
|
|
|
gammas[i] <- ((changepoint.ts[i] - m - sum(gammas))
|
|
|
* (1 - k.cum[i] / k.cum[i + 1]))
|
|
|
}
|
|
|
# Get cumulative rate and offset at each t
|
|
|
k_t <- rep(k, length(t))
|
|
|
m_t <- rep(m, length(t))
|
|
|
- for (s in seq_along(changepoint.ts)) {
|
|
|
+ for (s in 1:length(changepoint.ts)) {
|
|
|
indx <- t >= changepoint.ts[s]
|
|
|
k_t[indx] <- k_t[indx] + deltas[s]
|
|
|
m_t[indx] <- m_t[indx] + gammas[s]
|
|
@@ -1135,14 +1140,14 @@ predict_seasonal_components <- function(m, df) {
|
|
|
upper.p <- (1 + m$interval.width)/2
|
|
|
|
|
|
components <- dplyr::data_frame(component = colnames(seasonal.features)) %>%
|
|
|
- dplyr::mutate(col = seq_len(n())) %>%
|
|
|
+ dplyr::mutate(col = 1:n()) %>%
|
|
|
tidyr::separate(component, c('component', 'part'), sep = "_delim_",
|
|
|
extra = "merge", fill = "right") %>%
|
|
|
dplyr::select(col, component)
|
|
|
# Add total for all regression components
|
|
|
components <- rbind(
|
|
|
components,
|
|
|
- data.frame(col = seq_len(ncol(seasonal.features)), component = 'seasonal'))
|
|
|
+ data.frame(col = 1:ncol(seasonal.features), component = 'seasonal'))
|
|
|
# Add totals for seasonality, holiday, and extra regressors
|
|
|
components <- add_group_component(
|
|
|
components, 'seasonalities', names(m$seasonalities))
|
|
@@ -1159,7 +1164,7 @@ predict_seasonal_components <- function(m, df) {
|
|
|
dplyr::group_by(component) %>% dplyr::do({
|
|
|
comp <- (as.matrix(seasonal.features[, .$col])
|
|
|
%*% t(m$params$beta[, .$col, drop = FALSE])) * m$y.scale
|
|
|
- dplyr::data_frame(ix = seq_len(nrow(seasonal.features)),
|
|
|
+ dplyr::data_frame(ix = 1:nrow(seasonal.features),
|
|
|
mean = rowMeans(comp, na.rm = TRUE),
|
|
|
lower = apply(comp, 1, stats::quantile, lower.p,
|
|
|
na.rm = TRUE),
|
|
@@ -1213,9 +1218,9 @@ sample_posterior_predictive <- function(m, df) {
|
|
|
"seasonal" = matrix(, nrow = nrow(df), ncol = nsamp),
|
|
|
"yhat" = matrix(, nrow = nrow(df), ncol = nsamp))
|
|
|
|
|
|
- for (i in seq_len(n.iterations)) {
|
|
|
+ for (i in 1:n.iterations) {
|
|
|
# For each set of parameters from MCMC (or just 1 set for MAP),
|
|
|
- for (j in seq_len(samp.per.iter)) {
|
|
|
+ for (j in 1:samp.per.iter) {
|
|
|
# Do a simulation with this set of parameters,
|
|
|
sim <- sample_model(m, df, seasonal.features, i)
|
|
|
# Store the results
|
|
@@ -1383,6 +1388,310 @@ make_future_dataframe <- function(m, periods, freq = 'day',
|
|
|
return(data.frame(ds = dates))
|
|
|
}
|
|
|
|
|
|
+#' Merge history and forecast for plotting.
|
|
|
+#'
|
|
|
+#' @param m Prophet object.
|
|
|
+#' @param fcst Data frame returned by prophet predict.
|
|
|
+#'
|
|
|
+#' @importFrom dplyr "%>%"
|
|
|
+#' @keywords internal
|
|
|
+df_for_plotting <- function(m, fcst) {
|
|
|
+ # Make sure there is no y in fcst
|
|
|
+ fcst$y <- NULL
|
|
|
+ df <- m$history %>%
|
|
|
+ dplyr::select(ds, y) %>%
|
|
|
+ dplyr::full_join(fcst, by = "ds") %>%
|
|
|
+ dplyr::arrange(ds)
|
|
|
+ return(df)
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot the prophet forecast.
|
|
|
+#'
|
|
|
+#' @param x Prophet object.
|
|
|
+#' @param fcst Data frame returned by predict(m, df).
|
|
|
+#' @param uncertainty Boolean indicating if the uncertainty interval for yhat
|
|
|
+#' should be plotted. Must be present in fcst as yhat_lower and yhat_upper.
|
|
|
+#' @param plot_cap Boolean indicating if the capacity should be shown in the
|
|
|
+#' figure, if available.
|
|
|
+#' @param xlabel Optional label for x-axis
|
|
|
+#' @param ylabel Optional label for y-axis
|
|
|
+#' @param ... additional arguments
|
|
|
+#'
|
|
|
+#' @return A ggplot2 plot.
|
|
|
+#'
|
|
|
+#' @examples
|
|
|
+#' \dontrun{
|
|
|
+#' history <- data.frame(ds = seq(as.Date('2015-01-01'), as.Date('2016-01-01'), by = 'd'),
|
|
|
+#' y = sin(1:366/200) + rnorm(366)/10)
|
|
|
+#' m <- prophet(history)
|
|
|
+#' future <- make_future_dataframe(m, periods = 365)
|
|
|
+#' forecast <- predict(m, future)
|
|
|
+#' plot(m, forecast)
|
|
|
+#' }
|
|
|
+#'
|
|
|
+#' @export
|
|
|
+plot.prophet <- function(x, fcst, uncertainty = TRUE, plot_cap = TRUE,
|
|
|
+ xlabel = 'ds', ylabel = 'y', ...) {
|
|
|
+ df <- df_for_plotting(x, fcst)
|
|
|
+ gg <- ggplot2::ggplot(df, ggplot2::aes(x = ds, y = y)) +
|
|
|
+ ggplot2::labs(x = xlabel, y = ylabel)
|
|
|
+ if (exists('cap', where = df) && plot_cap) {
|
|
|
+ gg <- gg + ggplot2::geom_line(
|
|
|
+ ggplot2::aes(y = cap), linetype = 'dashed', na.rm = TRUE)
|
|
|
+ }
|
|
|
+ if (x$logistic.floor && exists('floor', where = df) && plot_cap) {
|
|
|
+ gg <- gg + ggplot2::geom_line(
|
|
|
+ ggplot2::aes(y = floor), linetype = 'dashed', na.rm = TRUE)
|
|
|
+ }
|
|
|
+ if (uncertainty && exists('yhat_lower', where = df)) {
|
|
|
+ gg <- gg +
|
|
|
+ ggplot2::geom_ribbon(ggplot2::aes(ymin = yhat_lower, ymax = yhat_upper),
|
|
|
+ alpha = 0.2,
|
|
|
+ fill = "#0072B2",
|
|
|
+ na.rm = TRUE)
|
|
|
+ }
|
|
|
+ gg <- gg +
|
|
|
+ ggplot2::geom_point(na.rm=TRUE) +
|
|
|
+ ggplot2::geom_line(ggplot2::aes(y = yhat), color = "#0072B2",
|
|
|
+ na.rm = TRUE) +
|
|
|
+ ggplot2::theme(aspect.ratio = 3 / 5)
|
|
|
+ return(gg)
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot the components of a prophet forecast.
|
|
|
+#' Prints a ggplot2 with panels for trend, weekly and yearly seasonalities if
|
|
|
+#' present, and holidays if present.
|
|
|
+#'
|
|
|
+#' @param m Prophet object.
|
|
|
+#' @param fcst Data frame returned by predict(m, df).
|
|
|
+#' @param uncertainty Boolean indicating if the uncertainty interval should be
|
|
|
+#' plotted for the trend, from fcst columns trend_lower and trend_upper.
|
|
|
+#' @param plot_cap Boolean indicating if the capacity should be shown in the
|
|
|
+#' figure, if available.
|
|
|
+#' @param weekly_start Integer specifying the start day of the weekly
|
|
|
+#' seasonality plot. 0 (default) starts the week on Sunday. 1 shifts by 1 day
|
|
|
+#' to Monday, and so on.
|
|
|
+#' @param yearly_start Integer specifying the start day of the yearly
|
|
|
+#' seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts by 1 day
|
|
|
+#' to Jan 2, and so on.
|
|
|
+#'
|
|
|
+#' @return Invisibly return a list containing the plotted ggplot objects
|
|
|
+#'
|
|
|
+#' @export
|
|
|
+#' @importFrom dplyr "%>%"
|
|
|
+prophet_plot_components <- function(
|
|
|
+ m, fcst, uncertainty = TRUE, plot_cap = TRUE, weekly_start = 0,
|
|
|
+ yearly_start = 0
|
|
|
+) {
|
|
|
+ # Plot the trend
|
|
|
+ panels <- list(plot_forecast_component(fcst, 'trend', uncertainty, plot_cap))
|
|
|
+ # Plot holiday components, if present.
|
|
|
+ if (!is.null(m$holidays) & ('holidays' %in% colnames(fcst))) {
|
|
|
+ panels[[length(panels) + 1]] <- plot_forecast_component(
|
|
|
+ fcst, 'holidays', uncertainty, FALSE)
|
|
|
+ }
|
|
|
+ # Plot weekly seasonality, if present
|
|
|
+ if ("weekly" %in% colnames(fcst)) {
|
|
|
+ panels[[length(panels) + 1]] <- plot_weekly(m, uncertainty, weekly_start)
|
|
|
+ }
|
|
|
+ # Plot yearly seasonality, if present
|
|
|
+ if ("yearly" %in% colnames(fcst)) {
|
|
|
+ panels[[length(panels) + 1]] <- plot_yearly(m, uncertainty, yearly_start)
|
|
|
+ }
|
|
|
+ # Plot other seasonalities
|
|
|
+ for (name in names(m$seasonalities)) {
|
|
|
+ if (!(name %in% c('weekly', 'yearly')) &&
|
|
|
+ (name %in% colnames(fcst))) {
|
|
|
+ panels[[length(panels) + 1]] <- plot_seasonality(m, name, uncertainty)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ # Plot extra regressors
|
|
|
+ if ((length(m$extra_regressors) > 0)
|
|
|
+ & ('extra_regressors' %in% colnames(fcst))) {
|
|
|
+ panels[[length(panels) + 1]] <- plot_forecast_component(
|
|
|
+ fcst, 'extra_regressors', uncertainty, FALSE)
|
|
|
+ }
|
|
|
+
|
|
|
+ # Make the plot.
|
|
|
+ grid::grid.newpage()
|
|
|
+ grid::pushViewport(grid::viewport(layout = grid::grid.layout(length(panels),
|
|
|
+ 1)))
|
|
|
+ for (i in 1:length(panels)) {
|
|
|
+ print(panels[[i]], vp = grid::viewport(layout.pos.row = i,
|
|
|
+ layout.pos.col = 1))
|
|
|
+ }
|
|
|
+ return(invisible(panels))
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot a particular component of the forecast.
|
|
|
+#'
|
|
|
+#' @param fcst Dataframe output of `predict`.
|
|
|
+#' @param name String name of the component to plot (column of fcst).
|
|
|
+#' @param uncertainty Boolean to plot uncertainty intervals.
|
|
|
+#' @param plot_cap Boolean indicating if the capacity should be shown in the
|
|
|
+#' figure, if available.
|
|
|
+#'
|
|
|
+#' @return A ggplot2 plot.
|
|
|
+#'
|
|
|
+#' @export
|
|
|
+plot_forecast_component <- function(
|
|
|
+ fcst, name, uncertainty = TRUE, plot_cap = FALSE
|
|
|
+) {
|
|
|
+ gg.comp <- ggplot2::ggplot(
|
|
|
+ fcst, ggplot2::aes_string(x = 'ds', y = name, group = 1)) +
|
|
|
+ ggplot2::geom_line(color = "#0072B2", na.rm = TRUE)
|
|
|
+ if (exists('cap', where = fcst) && plot_cap) {
|
|
|
+ gg.comp <- gg.comp + ggplot2::geom_line(
|
|
|
+ ggplot2::aes(y = cap), linetype = 'dashed', na.rm = TRUE)
|
|
|
+ }
|
|
|
+ if (exists('floor', where = fcst) && plot_cap) {
|
|
|
+ gg.comp <- gg.comp + ggplot2::geom_line(
|
|
|
+ ggplot2::aes(y = floor), linetype = 'dashed', na.rm = TRUE)
|
|
|
+ }
|
|
|
+ if (uncertainty) {
|
|
|
+ gg.comp <- gg.comp +
|
|
|
+ ggplot2::geom_ribbon(
|
|
|
+ ggplot2::aes_string(
|
|
|
+ ymin = paste0(name, '_lower'), ymax = paste0(name, '_upper')
|
|
|
+ ),
|
|
|
+ alpha = 0.2,
|
|
|
+ fill = "#0072B2",
|
|
|
+ na.rm = TRUE)
|
|
|
+ }
|
|
|
+ return(gg.comp)
|
|
|
+}
|
|
|
+
|
|
|
+#' Prepare dataframe for plotting seasonal components.
|
|
|
+#'
|
|
|
+#' @param m Prophet object.
|
|
|
+#' @param ds Array of dates for column ds.
|
|
|
+#'
|
|
|
+#' @return A dataframe with seasonal components on ds.
|
|
|
+#'
|
|
|
+#' @keywords internal
|
|
|
+seasonality_plot_df <- function(m, ds) {
|
|
|
+ df_list <- list(ds = ds, cap = 1, floor = 0)
|
|
|
+ for (name in names(m$extra_regressors)) {
|
|
|
+ df_list[[name]] <- 0
|
|
|
+ }
|
|
|
+ df <- as.data.frame(df_list)
|
|
|
+ df <- setup_dataframe(m, df)$df
|
|
|
+ return(df)
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot the weekly component of the forecast.
|
|
|
+#'
|
|
|
+#' @param m Prophet model object
|
|
|
+#' @param uncertainty Boolean to plot uncertainty intervals.
|
|
|
+#' @param weekly_start Integer specifying the start day of the weekly
|
|
|
+#' seasonality plot. 0 (default) starts the week on Sunday. 1 shifts by 1 day
|
|
|
+#' to Monday, and so on.
|
|
|
+#'
|
|
|
+#' @return A ggplot2 plot.
|
|
|
+#'
|
|
|
+#' @keywords internal
|
|
|
+plot_weekly <- function(m, uncertainty = TRUE, weekly_start = 0) {
|
|
|
+ # Compute weekly seasonality for a Sun-Sat sequence of dates.
|
|
|
+ days <- seq(set_date('2017-01-01'), by='d', length.out=7) + as.difftime(
|
|
|
+ weekly_start, units = "days")
|
|
|
+ df.w <- seasonality_plot_df(m, days)
|
|
|
+ seas <- predict_seasonal_components(m, df.w)
|
|
|
+ seas$dow <- factor(weekdays(df.w$ds), levels=weekdays(df.w$ds))
|
|
|
+
|
|
|
+ gg.weekly <- ggplot2::ggplot(seas, ggplot2::aes(x = dow, y = weekly,
|
|
|
+ group = 1)) +
|
|
|
+ ggplot2::geom_line(color = "#0072B2", na.rm = TRUE) +
|
|
|
+ ggplot2::labs(x = "Day of week")
|
|
|
+ if (uncertainty) {
|
|
|
+ gg.weekly <- gg.weekly +
|
|
|
+ ggplot2::geom_ribbon(ggplot2::aes(ymin = weekly_lower,
|
|
|
+ ymax = weekly_upper),
|
|
|
+ alpha = 0.2,
|
|
|
+ fill = "#0072B2",
|
|
|
+ na.rm = TRUE)
|
|
|
+ }
|
|
|
+ return(gg.weekly)
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot the yearly component of the forecast.
|
|
|
+#'
|
|
|
+#' @param m Prophet model object.
|
|
|
+#' @param uncertainty Boolean to plot uncertainty intervals.
|
|
|
+#' @param yearly_start Integer specifying the start day of the yearly
|
|
|
+#' seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts by 1 day
|
|
|
+#' to Jan 2, and so on.
|
|
|
+#'
|
|
|
+#' @return A ggplot2 plot.
|
|
|
+#'
|
|
|
+#' @keywords internal
|
|
|
+plot_yearly <- function(m, uncertainty = TRUE, yearly_start = 0) {
|
|
|
+ # Compute yearly seasonality for a Jan 1 - Dec 31 sequence of dates.
|
|
|
+ days <- seq(set_date('2017-01-01'), by='d', length.out=365) + as.difftime(
|
|
|
+ yearly_start, units = "days")
|
|
|
+ df.y <- seasonality_plot_df(m, days)
|
|
|
+ seas <- predict_seasonal_components(m, df.y)
|
|
|
+ seas$ds <- df.y$ds
|
|
|
+
|
|
|
+ gg.yearly <- ggplot2::ggplot(seas, ggplot2::aes(x = ds, y = yearly,
|
|
|
+ group = 1)) +
|
|
|
+ ggplot2::geom_line(color = "#0072B2", na.rm = TRUE) +
|
|
|
+ ggplot2::labs(x = "Day of year") +
|
|
|
+ ggplot2::scale_x_datetime(labels = scales::date_format('%B %d'))
|
|
|
+ if (uncertainty) {
|
|
|
+ gg.yearly <- gg.yearly +
|
|
|
+ ggplot2::geom_ribbon(ggplot2::aes(ymin = yearly_lower,
|
|
|
+ ymax = yearly_upper),
|
|
|
+ alpha = 0.2,
|
|
|
+ fill = "#0072B2",
|
|
|
+ na.rm = TRUE)
|
|
|
+ }
|
|
|
+ return(gg.yearly)
|
|
|
+}
|
|
|
+
|
|
|
+#' Plot a custom seasonal component.
|
|
|
+#'
|
|
|
+#' @param m Prophet model object.
|
|
|
+#' @param name String name of the seasonality.
|
|
|
+#' @param uncertainty Boolean to plot uncertainty intervals.
|
|
|
+#'
|
|
|
+#' @return A ggplot2 plot.
|
|
|
+#'
|
|
|
+#' @keywords internal
|
|
|
+plot_seasonality <- function(m, name, uncertainty = TRUE) {
|
|
|
+ # Compute seasonality from Jan 1 through a single period.
|
|
|
+ start <- set_date('2017-01-01')
|
|
|
+ period <- m$seasonalities[[name]]$period
|
|
|
+ end <- start + period * 24 * 3600
|
|
|
+ plot.points <- 200
|
|
|
+ days <- seq(from=start, to=end, length.out=plot.points)
|
|
|
+ df.y <- seasonality_plot_df(m, days)
|
|
|
+ seas <- predict_seasonal_components(m, df.y)
|
|
|
+ seas$ds <- df.y$ds
|
|
|
+ gg.s <- ggplot2::ggplot(
|
|
|
+ seas, ggplot2::aes_string(x = 'ds', y = name, group = 1)) +
|
|
|
+ ggplot2::geom_line(color = "#0072B2", na.rm = TRUE)
|
|
|
+ if (period <= 2) {
|
|
|
+ fmt.str <- '%T'
|
|
|
+ } else if (period < 14) {
|
|
|
+ fmt.str <- '%m/%d %R'
|
|
|
+ } else {
|
|
|
+ fmt.str <- '%m/%d'
|
|
|
+ }
|
|
|
+ gg.s <- gg.s +
|
|
|
+ ggplot2::scale_x_datetime(labels = scales::date_format(fmt.str))
|
|
|
+ if (uncertainty) {
|
|
|
+ gg.s <- gg.s +
|
|
|
+ ggplot2::geom_ribbon(
|
|
|
+ ggplot2::aes_string(
|
|
|
+ ymin = paste0(name, '_lower'), ymax = paste0(name, '_upper')
|
|
|
+ ),
|
|
|
+ alpha = 0.2,
|
|
|
+ fill = "#0072B2",
|
|
|
+ na.rm = TRUE)
|
|
|
+ }
|
|
|
+ return(gg.s)
|
|
|
+}
|
|
|
+
|
|
|
#' Copy Prophet object.
|
|
|
#'
|
|
|
#' @param m Prophet model object.
|