prophet.plot

   1# Copyright (c) Facebook, Inc. and its affiliates.
   2
   3# This source code is licensed under the MIT license found in the
   4# LICENSE file in the root directory of this source tree.
   5
   6from __future__ import annotations
   7
   8import logging
   9from typing import TYPE_CHECKING, cast
  10
  11import numpy as np
  12import pandas as pd
  13
  14# TODO: separate performance_metrics into a different module. there is an implicit circular import between forecaster.py and diagnostics.py
  15from prophet.diagnostics import performance_metrics
  16
  17if TYPE_CHECKING:
  18    from typing import Literal, Sequence, TypeVar, type_check_only
  19    from typing_extensions import TypedDict
  20    
  21    from prophet.forecaster import Prophet
  22    
  23    import matplotlib.pyplot as plt
  24    import plotly.graph_objs as go
  25
  26    _AxT = TypeVar('_AxT', bound=plt.Axes)
  27
  28    @type_check_only
  29    class _PlotlyProps(TypedDict):
  30        traces: list[go.Scatter]
  31        xaxis: go.layout.XAxis
  32        yaxis: go.layout.YAxis
  33
  34
  35logger: logging.Logger = logging.getLogger('prophet.plot')
  36
  37
  38try:
  39    from matplotlib import pyplot as plt
  40    from matplotlib.dates import (
  41        MonthLocator,
  42        num2date,
  43        AutoDateLocator,
  44        AutoDateFormatter,
  45    )
  46    from matplotlib.ticker import FuncFormatter
  47
  48    from pandas.plotting import deregister_matplotlib_converters
  49    deregister_matplotlib_converters()
  50except ImportError:
  51    logger.error('Importing matplotlib failed. Plotting will not work.')
  52
  53try:
  54    import plotly.graph_objs as go
  55    from plotly.subplots import make_subplots
  56except ImportError:
  57    logger.error('Importing plotly failed. Interactive plots will not work.')
  58
  59
  60def plot(
  61    m: Prophet,
  62    fcst: pd.DataFrame,
  63    ax: plt.Axes | None = None,
  64    uncertainty: bool = True,
  65    plot_cap: bool = True,
  66    xlabel: str = "ds",
  67    ylabel: str = "y",
  68    figsize: tuple[int, int] = (10, 6),
  69    include_legend: bool = False,
  70) -> plt.Figure:
  71    """Plot the Prophet forecast.
  72
  73    Parameters
  74    ----------
  75    m: Prophet model.
  76    fcst: pd.DataFrame output of m.predict.
  77    ax: Optional matplotlib axes on which to plot.
  78    uncertainty: Optional boolean to plot uncertainty intervals, which will
  79        only be done if m.uncertainty_samples > 0.
  80    plot_cap: Optional boolean indicating if the capacity should be shown
  81        in the figure, if available.
  82    xlabel: Optional label name on X-axis
  83    ylabel: Optional label name on Y-axis
  84    figsize: Optional tuple width, height in inches.
  85    include_legend: Optional boolean to add legend to the plot.
  86
  87    Returns
  88    -------
  89    A matplotlib figure.
  90    """
  91    user_provided_ax = False if ax is None else True
  92    if ax is None:
  93        fig = plt.figure(facecolor='w', figsize=figsize)
  94        ax = fig.add_subplot(111)
  95    else:
  96        fig = cast('plt.Figure', ax.get_figure())
  97    fcst_t = fcst['ds']
  98    history = cast('pd.DataFrame', m.history)
  99    ax.plot(history['ds'], history['y'], 'k.', label='Observed data points')
 100    ax.plot(fcst_t, fcst['yhat'], ls='-', c='#0072B2', label='Forecast')
 101    if 'cap' in fcst and plot_cap:
 102        ax.plot(fcst_t, fcst['cap'], ls='--', c='k', label='Maximum capacity')
 103    if m.logistic_floor and 'floor' in fcst and plot_cap:
 104        ax.plot(fcst_t, fcst['floor'], ls='--', c='k', label='Minimum capacity')
 105    if uncertainty and m.uncertainty_samples:
 106        ax.fill_between(fcst_t, fcst['yhat_lower'], fcst['yhat_upper'],
 107                        color='#0072B2', alpha=0.2, label='Uncertainty interval')
 108    # Specify formatting to workaround matplotlib issue #12925
 109    locator = AutoDateLocator(interval_multiples=False)
 110    formatter = AutoDateFormatter(locator)
 111    ax.xaxis.set_major_locator(locator)
 112    ax.xaxis.set_major_formatter(formatter)
 113    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
 114    ax.set_xlabel(xlabel)
 115    ax.set_ylabel(ylabel)
 116    if include_legend:
 117        ax.legend()
 118    if not user_provided_ax:
 119        try:
 120            if fig.get_layout_engine() is None:
 121                fig.tight_layout()
 122        except AttributeError:
 123            fig.tight_layout()
 124    return fig
 125
 126
 127def plot_components(
 128    m: Prophet,
 129    fcst: pd.DataFrame,
 130    uncertainty: bool = True,
 131    plot_cap: bool = True,
 132    weekly_start: int = 0,
 133    yearly_start: int = 0,
 134    figsize: tuple[int, int] | None = None,
 135) -> plt.Figure:
 136    """Plot the Prophet forecast components.
 137
 138    Will plot whichever are available of: trend, holidays, weekly
 139    seasonality, yearly seasonality, and additive and multiplicative extra
 140    regressors.
 141
 142    Parameters
 143    ----------
 144    m: Prophet model.
 145    fcst: pd.DataFrame output of m.predict.
 146    uncertainty: Optional boolean to plot uncertainty intervals, which will
 147        only be done if m.uncertainty_samples > 0.
 148    plot_cap: Optional boolean indicating if the capacity should be shown
 149        in the figure, if available.
 150    weekly_start: Optional int specifying the start day of the weekly
 151        seasonality plot. 0 (default) starts the week on Sunday. 1 shifts
 152        by 1 day to Monday, and so on.
 153    yearly_start: Optional int specifying the start day of the yearly
 154        seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts
 155        by 1 day to Jan 2, and so on.
 156    figsize: Optional tuple width, height in inches.
 157
 158    Returns
 159    -------
 160    A matplotlib figure.
 161    """
 162    # Identify components to be plotted
 163    components = ['trend']
 164    if m.train_holiday_names is not None and 'holidays' in fcst:
 165        components.append('holidays')
 166    # Plot weekly seasonality, if present
 167    if 'weekly' in m.seasonalities and 'weekly' in fcst:
 168        components.append('weekly')
 169    # Yearly if present
 170    if 'yearly' in m.seasonalities and 'yearly' in fcst:
 171        components.append('yearly')
 172    # Other seasonalities
 173    components.extend([
 174        name for name in sorted(m.seasonalities)
 175        if name in fcst and name not in ['weekly', 'yearly']
 176    ])
 177    regressors = {'additive': False, 'multiplicative': False}
 178    for name, props in m.extra_regressors.items():
 179        regressors[props['mode']] = True
 180    for mode in ['additive', 'multiplicative']:
 181        if regressors[mode] and 'extra_regressors_{}'.format(mode) in fcst:
 182            components.append('extra_regressors_{}'.format(mode))
 183    npanel = len(components)
 184
 185    figsize = figsize if figsize else (9, 3 * npanel)
 186    fig, axes = plt.subplots(npanel, 1, facecolor='w', figsize=figsize)
 187
 188    if npanel == 1:
 189        axes = [axes]
 190
 191    multiplicative_axes = []
 192
 193    dt = cast('pd.DataFrame', m.history)['ds'].diff()
 194    min_dt = dt.iloc[cast('np.ndarray', dt.values).nonzero()[0]].min() 
 195
 196    for ax, plot_name in zip(axes, components):
 197        if plot_name == 'trend':
 198            plot_forecast_component(
 199                m=m, fcst=fcst, name='trend', ax=ax, uncertainty=uncertainty,
 200                plot_cap=plot_cap,
 201            )
 202        elif plot_name in m.seasonalities:
 203            if (
 204                (plot_name == 'weekly' or m.seasonalities[plot_name]['period'] == 7)
 205                and (min_dt == pd.Timedelta(days=1))
 206            ):
 207                plot_weekly(
 208                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty, weekly_start=weekly_start
 209                )
 210            elif plot_name == 'yearly' or m.seasonalities[plot_name]['period'] == 365.25:
 211                plot_yearly(
 212                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty, yearly_start=yearly_start
 213                )
 214            else:
 215                plot_seasonality(
 216                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty,
 217                )
 218        elif plot_name in [
 219            'holidays',
 220            'extra_regressors_additive',
 221            'extra_regressors_multiplicative',
 222        ]:
 223            plot_forecast_component(
 224                m=m, fcst=fcst, name=plot_name, ax=ax, uncertainty=uncertainty,
 225                plot_cap=False,
 226            )
 227        assert m.component_modes is not None
 228        if plot_name in m.component_modes['multiplicative']:
 229            multiplicative_axes.append(ax)
 230
 231    try:
 232        if fig.get_layout_engine() is None:
 233            fig.tight_layout()
 234    except AttributeError:
 235        fig.tight_layout()
 236    # Reset multiplicative axes labels after tight_layout adjustment
 237    for ax in multiplicative_axes:
 238        ax = set_y_as_percent(ax)
 239    return fig
 240
 241
 242def plot_forecast_component(
 243    m: Prophet,
 244    fcst: pd.DataFrame,
 245    name: str,
 246    ax: plt.Axes | None = None,
 247    uncertainty: bool = True,
 248    plot_cap: bool = False,
 249    figsize: tuple[int, int] = (10, 6),
 250) -> Sequence[plt.Artist]:
 251    """Plot a particular component of the forecast.
 252
 253    Parameters
 254    ----------
 255    m: Prophet model.
 256    fcst: pd.DataFrame output of m.predict.
 257    name: Name of the component to plot.
 258    ax: Optional matplotlib Axes to plot on.
 259    uncertainty: Optional boolean to plot uncertainty intervals, which will
 260        only be done if m.uncertainty_samples > 0.
 261    plot_cap: Optional boolean indicating if the capacity should be shown
 262        in the figure, if available.
 263    figsize: Optional tuple width, height in inches.
 264
 265    Returns
 266    -------
 267    a list of matplotlib artists
 268    """
 269    artists = []
 270    if not ax:
 271        fig = plt.figure(facecolor='w', figsize=figsize)
 272        ax = fig.add_subplot(111)
 273    fcst_t = fcst['ds']
 274    artists += ax.plot(fcst_t, fcst[name], ls='-', c='#0072B2')
 275    if 'cap' in fcst and plot_cap:
 276        artists += ax.plot(fcst_t, fcst['cap'], ls='--', c='k')
 277    if m.logistic_floor and 'floor' in fcst and plot_cap:
 278        ax.plot(fcst_t, fcst['floor'], ls='--', c='k')
 279    if uncertainty and m.uncertainty_samples:
 280        artists += [ax.fill_between(
 281            fcst_t, fcst[name + '_lower'], fcst[name + '_upper'],
 282            color='#0072B2', alpha=0.2)]
 283    # Specify formatting to workaround matplotlib issue #12925
 284    locator = AutoDateLocator(interval_multiples=False)
 285    formatter = AutoDateFormatter(locator)
 286    ax.xaxis.set_major_locator(locator)
 287    ax.xaxis.set_major_formatter(formatter)
 288    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
 289    ax.set_xlabel('ds')
 290    ax.set_ylabel(name)
 291    assert m.component_modes
 292    if name in m.component_modes['multiplicative']:
 293        ax = set_y_as_percent(ax)
 294    return artists
 295
 296
 297def seasonality_plot_df(
 298    m: Prophet,
 299    ds: Sequence[pd.Timestamp] | pd.DatetimeIndex,
 300) -> pd.DataFrame:
 301    """Prepare dataframe for plotting seasonal components.
 302
 303    Parameters
 304    ----------
 305    m: Prophet model.
 306    ds: List of dates for column ds.
 307
 308    Returns
 309    -------
 310    A dataframe with seasonal components on ds.
 311    """
 312    df_dict = {'ds': ds, 'cap': 1., 'floor': 0.}
 313    for name in m.extra_regressors:
 314        df_dict[name] = 0.
 315    # Activate all conditional seasonality columns
 316    for props in m.seasonalities.values():
 317        if props['condition_name'] is not None:
 318            df_dict[props['condition_name']] = True
 319    df = pd.DataFrame(df_dict)
 320    df = m.setup_dataframe(df)
 321    return df
 322
 323
 324def plot_weekly(
 325    m: Prophet,
 326    ax: plt.Axes | None = None,
 327    uncertainty: bool = True,
 328    weekly_start: int = 0,
 329    figsize: tuple[int, int] = (10, 6),
 330    name: str = 'weekly',
 331) -> Sequence[plt.Artist]:
 332    """Plot the weekly component of the forecast.
 333
 334    Parameters
 335    ----------
 336    m: Prophet model.
 337    ax: Optional matplotlib Axes to plot on. One will be created if this
 338        is not provided.
 339    uncertainty: Optional boolean to plot uncertainty intervals, which will
 340        only be done if m.uncertainty_samples > 0.
 341    weekly_start: Optional int specifying the start day of the weekly
 342        seasonality plot. 0 (default) starts the week on Sunday. 1 shifts
 343        by 1 day to Monday, and so on.
 344    figsize: Optional tuple width, height in inches.
 345    name: Name of seasonality component if changed from default 'weekly'.
 346
 347    Returns
 348    -------
 349    a list of matplotlib artists
 350    """
 351    artists = []
 352    if not ax:
 353        fig = plt.figure(facecolor='w', figsize=figsize)
 354        ax = fig.add_subplot(111)
 355    # Compute weekly seasonality for a Sun-Sat sequence of dates.
 356    days = (pd.date_range(start='2017-01-01', periods=7) +
 357            pd.Timedelta(days=weekly_start))
 358    df_w = seasonality_plot_df(m, days)
 359    seas = m.predict_seasonal_components(df_w)
 360    days = days.day_name()
 361    artists += ax.plot(range(len(days)), seas[name], ls='-',
 362                    c='#0072B2')
 363    if uncertainty and m.uncertainty_samples:
 364        artists += [ax.fill_between(range(len(days)),
 365                                    seas[name + '_lower'], seas[name + '_upper'],
 366                                    color='#0072B2', alpha=0.2)]
 367    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
 368    ax.set_xticks(range(len(days)))
 369    ax.set_xticklabels(days)
 370    ax.set_xlabel('Day of week')
 371    ax.set_ylabel(name)
 372    if m.seasonalities[name]['mode'] == 'multiplicative':
 373        ax = set_y_as_percent(ax)
 374    return artists
 375
 376
 377def plot_yearly(
 378    m: Prophet,
 379    ax: plt.Axes | None = None,
 380    uncertainty: bool = True,
 381    yearly_start: int = 0,
 382    figsize: tuple[int, int] = (10, 6),
 383    name: str = 'yearly',
 384) -> Sequence[plt.Artist]:
 385    """Plot the yearly component of the forecast.
 386
 387    Parameters
 388    ----------
 389    m: Prophet model.
 390    ax: Optional matplotlib Axes to plot on. One will be created if
 391        this is not provided.
 392    uncertainty: Optional boolean to plot uncertainty intervals, which will
 393        only be done if m.uncertainty_samples > 0.
 394    yearly_start: Optional int specifying the start day of the yearly
 395        seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts
 396        by 1 day to Jan 2, and so on.
 397    figsize: Optional tuple width, height in inches.
 398    name: Name of seasonality component if previously changed from default 'yearly'.
 399
 400    Returns
 401    -------
 402    a list of matplotlib artists
 403    """
 404    artists = []
 405    if not ax:
 406        fig = plt.figure(facecolor='w', figsize=figsize)
 407        ax = fig.add_subplot(111)
 408    # Compute yearly seasonality for a Jan 1 - Dec 31 sequence of dates.
 409    days = (pd.date_range(start='2017-01-01', periods=365) +
 410            pd.Timedelta(days=yearly_start))
 411    df_y = seasonality_plot_df(m, days)
 412    seas = m.predict_seasonal_components(df_y)
 413    artists += ax.plot(
 414        df_y['ds'], seas[name], ls='-', c='#0072B2')
 415    if uncertainty and m.uncertainty_samples:
 416        artists += [ax.fill_between(
 417            df_y['ds'], seas[name + '_lower'],
 418            seas[name + '_upper'], color='#0072B2', alpha=0.2)]
 419    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
 420    months = MonthLocator(range(1, 13), bymonthday=1, interval=2)
 421    ax.xaxis.set_major_formatter(FuncFormatter(
 422        lambda x, pos=None: '{dt:%B} {dt.day}'.format(dt=num2date(x))))
 423    ax.xaxis.set_major_locator(months)
 424    ax.set_xlabel('Day of year')
 425    ax.set_ylabel(name)
 426    if m.seasonalities[name]['mode'] == 'multiplicative':
 427        ax = set_y_as_percent(ax)
 428    return artists
 429
 430
 431def plot_seasonality(
 432    m: Prophet,
 433    name: str,
 434    ax: plt.Axes | None = None,
 435    uncertainty: bool = True,
 436    figsize: tuple[int, int] = (10, 6),
 437) -> Sequence[plt.Artist]:
 438    """Plot a custom seasonal component.
 439
 440    Parameters
 441    ----------
 442    m: Prophet model.
 443    name: Seasonality name, like 'daily', 'weekly'.
 444    ax: Optional matplotlib Axes to plot on. One will be created if
 445        this is not provided.
 446    uncertainty: Optional boolean to plot uncertainty intervals, which will
 447        only be done if m.uncertainty_samples > 0.
 448    figsize: Optional tuple width, height in inches.
 449
 450    Returns
 451    -------
 452    a list of matplotlib artists
 453    """
 454    artists = []
 455    if not ax:
 456        fig = plt.figure(facecolor='w', figsize=figsize)
 457        ax = fig.add_subplot(111)
 458    # Compute seasonality from Jan 1 through a single period.
 459    start = pd.to_datetime('2017-01-01 0000')
 460    period = m.seasonalities[name]['period']
 461    end = start + pd.Timedelta(days=period)
 462    plot_points = 200
 463    # https://github.com/pandas-dev/pandas-stubs/issues/1645
 464    days = pd.to_datetime(np.linspace(start.value, end.value, plot_points))  # pyrefly:ignore[no-matching-overload]
 465    df_y = seasonality_plot_df(m, days)
 466    seas = m.predict_seasonal_components(df_y)
 467    artists += ax.plot(df_y['ds'], seas[name], ls='-',
 468                        c='#0072B2')
 469    if uncertainty and m.uncertainty_samples:
 470        artists += [ax.fill_between(
 471            df_y['ds'], seas[name + '_lower'],
 472            seas[name + '_upper'], color='#0072B2', alpha=0.2)]
 473    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
 474    n_ticks = 8
 475    # https://github.com/pandas-dev/pandas-stubs/issues/1645
 476    xticks = pd.to_datetime(np.linspace(start.value, end.value, n_ticks)  # pyrefly:ignore[no-matching-overload]
 477        ).to_pydatetime()
 478    ax.set_xticks(xticks)
 479    if name == 'yearly':
 480        fmt = FuncFormatter(
 481            lambda x, pos=None: '{dt:%B} {dt.day}'.format(dt=num2date(x)))
 482        ax.set_xlabel('Day of year')
 483    elif name == 'weekly':
 484        fmt = FuncFormatter(
 485            lambda x, pos=None: '{dt:%A}'.format(dt=num2date(x)))
 486        ax.set_xlabel('Day of Week')
 487    elif name == 'daily':
 488        fmt = FuncFormatter(
 489            lambda x, pos=None: '{dt:%T}'.format(dt=num2date(x)))
 490        ax.set_xlabel('Hour of day')
 491    elif period <= 2:
 492        fmt = FuncFormatter(
 493            lambda x, pos=None: '{dt:%T}'.format(dt=num2date(x)))
 494        ax.set_xlabel('Hours')
 495    else:
 496        fmt = FuncFormatter(
 497            lambda x, pos=None: '{:.0f}'.format(pos * period / (n_ticks - 1)))
 498        ax.set_xlabel('Days')
 499    ax.xaxis.set_major_formatter(fmt)
 500    ax.set_ylabel(name)
 501    if m.seasonalities[name]['mode'] == 'multiplicative':
 502        ax = set_y_as_percent(ax)
 503    return artists
 504
 505
 506def set_y_as_percent(ax: _AxT) -> _AxT:
 507    yticks = 100 * ax.get_yticks()
 508    yticklabels = ['{0:.4g}%'.format(y) for y in yticks]
 509    ax.set_yticks(ax.get_yticks().tolist())
 510    ax.set_yticklabels(yticklabels)
 511    return ax
 512
 513
 514def add_changepoints_to_plot(
 515    ax: plt.Axes,
 516    m: Prophet,
 517    fcst: pd.DataFrame,
 518    threshold: float = 0.01,
 519    cp_color: str = 'r',
 520    cp_linestyle: str = '--',
 521    trend: bool = True,
 522) -> list[plt.Line2D]:
 523    """Add markers for significant changepoints to prophet forecast plot.
 524
 525    Example:
 526    fig = m.plot(forecast)
 527    add_changepoints_to_plot(fig.gca(), m, forecast)
 528
 529    Parameters
 530    ----------
 531    ax: axis on which to overlay changepoint markers.
 532    m: Prophet model.
 533    fcst: Forecast output from m.predict.
 534    threshold: Threshold on trend change magnitude for significance.
 535    cp_color: Color of changepoint markers.
 536    cp_linestyle: Linestyle for changepoint markers.
 537    trend: If True, will also overlay the trend.
 538
 539    Returns
 540    -------
 541    a list of matplotlib artists
 542    """
 543    artists = []
 544    if trend:
 545        artists.extend(ax.plot(fcst['ds'], fcst['trend'], c=cp_color))
 546    
 547    assert m.changepoints is not None
 548    signif_changepoints = m.changepoints[
 549        np.abs(np.nanmean(m.params['delta'], axis=0)) >= threshold
 550    ] if len(m.changepoints) > 0 else []
 551    for cp in signif_changepoints:
 552        # Matplotlib stubs type axvline x as float; pandas Timestamp is accepted at runtime.
 553        artists.append(ax.axvline(x=cp, c=cp_color, ls=cp_linestyle))  # pyrefly:ignore[bad-argument-type]
 554    return artists
 555
 556
 557def plot_cross_validation_metric(
 558    df_cv: pd.DataFrame,
 559    metric: str,
 560    rolling_window: float = 0.1,
 561    ax: plt.Axes | None = None,
 562    figsize: tuple[int, int] = (10, 6),
 563    color: str = 'b',
 564    point_color: str = 'gray',
 565) -> plt.Figure:
 566    """Plot a performance metric vs. forecast horizon from cross validation.
 567
 568    Cross validation produces a collection of out-of-sample model predictions
 569    that can be compared to actual values, at a range of different horizons
 570    (distance from the cutoff). This computes a specified performance metric
 571    for each prediction, and aggregated over a rolling window with horizon.
 572
 573    This uses prophet.diagnostics.performance_metrics to compute the metrics.
 574    Valid values of metric are 'mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', and 'coverage'.
 575
 576    rolling_window is the proportion of data included in the rolling window of
 577    aggregation. The default value of 0.1 means 10% of data are included in the
 578    aggregation for computing the metric.
 579
 580    As a concrete example, if metric='mse', then this plot will show the
 581    squared error for each cross validation prediction, along with the MSE
 582    averaged over rolling windows of 10% of the data.
 583
 584    Parameters
 585    ----------
 586    df_cv: The output from prophet.diagnostics.cross_validation.
 587    metric: Metric name, one of ['mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', 'coverage'].
 588    rolling_window: Proportion of data to use for rolling average of metric.
 589        In [0, 1]. Defaults to 0.1.
 590    ax: Optional matplotlib axis on which to plot. If not given, a new figure
 591        will be created.
 592    figsize: Optional tuple width, height in inches.
 593    color: Optional color for plot and error points, useful when plotting
 594        multiple model performances on one axis for comparison.
 595
 596    Returns
 597    -------
 598    a matplotlib figure.
 599    """
 600    if ax is None:
 601        fig = plt.figure(facecolor='w', figsize=figsize)
 602        ax = fig.add_subplot(111)
 603    else:
 604        fig = cast('plt.Figure', ax.get_figure())
 605    # Get the metric at the level of individual predictions, and with the rolling window.
 606    df_none = performance_metrics(df_cv, metrics=[metric], rolling_window=-1)
 607    df_h = performance_metrics(df_cv, metrics=[metric], rolling_window=rolling_window)
 608    
 609    assert df_none is not None
 610    assert df_h is not None
 611
 612    # Some work because matplotlib does not handle timedelta
 613    # Target ~10 ticks.
 614    tick_w = max(df_none['horizon'].astype('timedelta64[ns]')) / 10.
 615    # Find the largest time resolution that has <1 unit per bin.
 616    dts: list[Literal["D", "h", "m", "s", "ms", "us", "ns"]]
 617    dts = ['D', 'h', 'm', 's', 'ms', 'us', 'ns']
 618    dt_names = [
 619        'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds',
 620        'nanoseconds'
 621    ]
 622    dt_conversions = [
 623        24 * 60 * 60 * 10 ** 9,
 624        60 * 60 * 10 ** 9,
 625        60 * 10 ** 9,
 626        10 ** 9,
 627        10 ** 6,
 628        10 ** 3,
 629        1.,
 630    ]
 631    for i, dt in enumerate(dts):
 632        if np.timedelta64(1, dt) < np.timedelta64(tick_w, 'ns'):
 633            break
 634
 635    x_plt = np.asarray(df_none['horizon'].astype('timedelta64[ns]')).view(np.int64) / float(dt_conversions[i])
 636    x_plt_h = np.asarray(df_h['horizon'].astype('timedelta64[ns]')).view(np.int64) / float(dt_conversions[i])
 637
 638    ax.plot(x_plt, df_none[metric], '.', alpha=0.1, c=point_color)
 639    ax.plot(x_plt_h, df_h[metric], '-', c=color)
 640    ax.grid(True)
 641
 642    ax.set_xlabel('Horizon ({})'.format(dt_names[i]))
 643    ax.set_ylabel(metric)
 644    return fig
 645
 646
 647def plot_plotly(
 648    m: Prophet,
 649    fcst: pd.DataFrame,
 650    uncertainty: bool = True,
 651    plot_cap: bool = True,
 652    trend: bool = False,
 653    changepoints: bool = False,
 654    changepoints_threshold: float = 0.01,
 655    xlabel: str = 'ds',
 656    ylabel: str = 'y',
 657    figsize: tuple[int, int] = (900, 600)
 658) -> go.Figure:
 659    """Plot the Prophet forecast with Plotly offline.
 660
 661    Plotting in Jupyter Notebook requires initializing plotly.offline.init_notebook_mode():
 662    >>> import plotly.offline as py
 663    >>> py.init_notebook_mode()
 664    Then the figure can be displayed using plotly.offline.iplot(...):
 665    >>> fig = plot_plotly(m, fcst)
 666    >>> py.iplot(fig)
 667    see https://plot.ly/python/offline/ for details
 668
 669    Parameters
 670    ----------
 671    m: Prophet model.
 672    fcst: pd.DataFrame output of m.predict.
 673    uncertainty: Optional boolean to plot uncertainty intervals.
 674    plot_cap: Optional boolean indicating if the capacity should be shown
 675        in the figure, if available.
 676    trend: Optional boolean to plot trend
 677    changepoints: Optional boolean to plot changepoints
 678    changepoints_threshold: Threshold on trend change magnitude for significance.
 679    xlabel: Optional label name on X-axis
 680    ylabel: Optional label name on Y-axis
 681    figsize: The plot's size (in px).
 682
 683    Returns
 684    -------
 685    A Plotly Figure.
 686    """
 687    prediction_color = '#0072B2'
 688    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
 689    actual_color = 'black'
 690    cap_color = 'black'
 691    trend_color = '#B23B00'
 692    line_width = 2
 693    marker_size = 4
 694
 695    data = []
 696    # Add actual
 697    assert m.history
 698    data.append(go.Scatter(
 699        name='Actual',
 700        x=m.history['ds'],
 701        y=m.history['y'],
 702        marker=dict(color=actual_color, size=marker_size),
 703        mode='markers'
 704    ))
 705    # Add lower bound
 706    if uncertainty and m.uncertainty_samples:
 707        data.append(go.Scatter(
 708            x=fcst['ds'],
 709            y=fcst['yhat_lower'],
 710            mode='lines',
 711            line=dict(width=0),
 712            hoverinfo='skip'
 713        ))
 714    # Add prediction
 715    data.append(go.Scatter(
 716        name='Predicted',
 717        x=fcst['ds'],
 718        y=fcst['yhat'],
 719        mode='lines',
 720        line=dict(color=prediction_color, width=line_width),
 721        fillcolor=error_color,
 722        fill='tonexty' if uncertainty and m.uncertainty_samples else 'none'
 723    ))
 724    # Add upper bound
 725    if uncertainty and m.uncertainty_samples:
 726        data.append(go.Scatter(
 727            x=fcst['ds'],
 728            y=fcst['yhat_upper'],
 729            mode='lines',
 730            line=dict(width=0),
 731            fillcolor=error_color,
 732            fill='tonexty',
 733            hoverinfo='skip'
 734        ))
 735    # Add caps
 736    if 'cap' in fcst and plot_cap:
 737        data.append(go.Scatter(
 738            name='Cap',
 739            x=fcst['ds'],
 740            y=fcst['cap'],
 741            mode='lines',
 742            line=dict(color=cap_color, dash='dash', width=line_width),
 743        ))
 744    if m.logistic_floor and 'floor' in fcst and plot_cap:
 745        data.append(go.Scatter(
 746            name='Floor',
 747            x=fcst['ds'],
 748            y=fcst['floor'],
 749            mode='lines',
 750            line=dict(color=cap_color, dash='dash', width=line_width),
 751        ))
 752    # Add trend
 753    if trend:
 754        data.append(go.Scatter(
 755            name='Trend',
 756            x=fcst['ds'],
 757            y=fcst['trend'],
 758            mode='lines',
 759            line=dict(color=trend_color, width=line_width),
 760        ))
 761    # Add changepoints
 762    assert m.changepoints
 763    if changepoints and len(m.changepoints) > 0:
 764        signif_changepoints = m.changepoints[
 765            np.abs(np.nanmean(m.params['delta'], axis=0)) >= changepoints_threshold
 766        ]
 767        data.append(go.Scatter(
 768            x=signif_changepoints,
 769            y=fcst.loc[fcst['ds'].isin(signif_changepoints), 'trend'],
 770            marker=dict(size=50, symbol='line-ns-open', color=trend_color,
 771                        line=dict(width=line_width)),
 772            mode='markers',
 773            hoverinfo='skip'
 774        ))
 775
 776    layout = dict(
 777        showlegend=False,
 778        width=figsize[0],
 779        height=figsize[1],
 780        yaxis=dict(
 781            title=ylabel
 782        ),
 783        xaxis=dict(
 784            title=xlabel,
 785            type='date',
 786            rangeselector=dict(
 787                buttons=list([
 788                    dict(count=7,
 789                         label='1w',
 790                         step='day',
 791                         stepmode='backward'),
 792                    dict(count=1,
 793                         label='1m',
 794                         step='month',
 795                         stepmode='backward'),
 796                    dict(count=6,
 797                         label='6m',
 798                         step='month',
 799                         stepmode='backward'),
 800                    dict(count=1,
 801                         label='1y',
 802                         step='year',
 803                         stepmode='backward'),
 804                    dict(step='all')
 805                ])
 806            ),
 807            rangeslider=dict(
 808                visible=True
 809            ),
 810        ),
 811    )
 812    fig = go.Figure(data=data, layout=layout)
 813    return fig
 814
 815
 816def plot_components_plotly(
 817    m: Prophet,
 818    fcst: pd.DataFrame,
 819    uncertainty: bool = True,
 820    plot_cap: bool = True,
 821    figsize: tuple[int, int] = (900, 200),
 822) -> go.Figure:
 823    """Plot the Prophet forecast components using Plotly.
 824    See plot_plotly() for Plotly setup instructions
 825
 826    Will plot whichever are available of: trend, holidays, weekly
 827    seasonality, yearly seasonality, and additive and multiplicative extra
 828    regressors.
 829
 830    Parameters
 831    ----------
 832    m: Prophet model.
 833    fcst: pd.DataFrame output of m.predict.
 834    uncertainty: Optional boolean to plot uncertainty intervals, which will
 835        only be done if m.uncertainty_samples > 0.
 836    plot_cap: Optional boolean indicating if the capacity should be shown
 837        in the figure, if available.
 838    figsize: Set the size for the subplots (in px).
 839
 840    Returns
 841    -------
 842    A Plotly Figure.
 843    """
 844
 845    # Identify components to plot and get their Plotly props
 846    components = {}
 847    components['trend'] = get_forecast_component_plotly_props(
 848        m, fcst, 'trend', uncertainty, plot_cap)
 849    if m.train_holiday_names is not None and 'holidays' in fcst:
 850        components['holidays'] = get_forecast_component_plotly_props(
 851            m, fcst, 'holidays', uncertainty)
 852
 853    regressors = {'additive': False, 'multiplicative': False}
 854    for name, props in m.extra_regressors.items():
 855        regressors[props['mode']] = True
 856    for mode in ['additive', 'multiplicative']:
 857        if regressors[mode] and 'extra_regressors_{}'.format(mode) in fcst:
 858            components['extra_regressors_{}'.format(mode)] = get_forecast_component_plotly_props(
 859                m, fcst, 'extra_regressors_{}'.format(mode))
 860    for seasonality in m.seasonalities:
 861        components[seasonality] = get_seasonality_plotly_props(m, seasonality)
 862
 863    # Create Plotly subplot figure and add the components to it
 864    fig = make_subplots(rows=len(components), cols=1, print_grid=False)
 865    fig['layout'].update(go.Layout(
 866        showlegend=False,
 867        width=figsize[0],
 868        height=figsize[1] * len(components)
 869    ))
 870    for i, name in enumerate(components):
 871        if i == 0:
 872            xaxis = fig['layout']['xaxis']
 873            yaxis = fig['layout']['yaxis']
 874        else:
 875            xaxis = fig['layout']['xaxis{}'.format(i + 1)]
 876            yaxis = fig['layout']['yaxis{}'.format(i + 1)]
 877        xaxis.update(components[name]['xaxis'])
 878        yaxis.update(components[name]['yaxis'])
 879        for trace in components[name]['traces']:
 880            fig.append_trace(trace, i + 1, 1)
 881    return fig
 882
 883
 884def plot_forecast_component_plotly(
 885    m: Prophet,
 886    fcst: pd.DataFrame,
 887    name: str,
 888    uncertainty: bool = True,
 889    plot_cap: bool = False,
 890    figsize: tuple[int, int] = (900, 300)
 891) -> go.Figure:
 892    """Plot a particular component of the forecast using Plotly.
 893    See plot_plotly() for Plotly setup instructions
 894
 895    Parameters
 896    ----------
 897    m: Prophet model.
 898    fcst: pd.DataFrame output of m.predict.
 899    name: Name of the component to plot.
 900    uncertainty: Optional boolean to plot uncertainty intervals, which will
 901        only be done if m.uncertainty_samples > 0.
 902    plot_cap: Optional boolean indicating if the capacity should be shown
 903        in the figure, if available.
 904    figsize: The plot's size (in px).
 905
 906    Returns
 907    -------
 908    A Plotly Figure.
 909    """
 910    props = get_forecast_component_plotly_props(m, fcst, name, uncertainty, plot_cap)
 911    layout = go.Layout(
 912        width=figsize[0],
 913        height=figsize[1],
 914        showlegend=False,
 915        xaxis=props['xaxis'],
 916        yaxis=props['yaxis']
 917    )
 918    fig = go.Figure(data=props['traces'], layout=layout)
 919    return fig
 920
 921
 922def plot_seasonality_plotly(
 923    m: Prophet,
 924    name: str,
 925    uncertainty: bool = True,
 926    figsize: tuple[int, int] = (900, 300)
 927) -> go.Figure:
 928    """Plot a custom seasonal component using Plotly.
 929    See plot_plotly() for Plotly setup instructions
 930
 931    Parameters
 932    ----------
 933    m: Prophet model.
 934    name: Seasonality name, like 'daily', 'weekly'.
 935    uncertainty: Optional boolean to plot uncertainty intervals, which will
 936        only be done if m.uncertainty_samples > 0.
 937    figsize: Set the plot's size (in px).
 938
 939    Returns
 940    -------
 941    A Plotly Figure.
 942    """
 943    props = get_seasonality_plotly_props(m, name, uncertainty)
 944    layout = go.Layout(
 945        width=figsize[0],
 946        height=figsize[1],
 947        showlegend=False,
 948        xaxis=props['xaxis'],
 949        yaxis=props['yaxis']
 950    )
 951    fig = go.Figure(data=props['traces'], layout=layout)
 952    return fig
 953
 954
 955def get_forecast_component_plotly_props(
 956    m: Prophet,
 957    fcst: pd.DataFrame,
 958    name: str,
 959    uncertainty: bool = True,
 960    plot_cap: bool = False,
 961) -> _PlotlyProps:
 962    """Prepares a dictionary for plotting the selected forecast component with Plotly
 963
 964    Parameters
 965    ----------
 966    m: Prophet model.
 967    fcst: pd.DataFrame output of m.predict.
 968    name: Name of the component to plot.
 969    uncertainty: Optional boolean to plot uncertainty intervals, which will
 970        only be done if m.uncertainty_samples > 0.
 971    plot_cap: Optional boolean indicating if the capacity should be shown
 972        in the figure, if available.
 973
 974    Returns
 975    -------
 976    A dictionary with Plotly traces, xaxis and yaxis
 977    """
 978    prediction_color = '#0072B2'
 979    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
 980    cap_color = 'black'
 981    zeroline_color = '#AAA'
 982    line_width = 2
 983
 984    range_margin = (fcst['ds'].max() - fcst['ds'].min()) * 0.05
 985    range_x = [fcst['ds'].min() - range_margin, fcst['ds'].max() + range_margin]
 986
 987    text = None
 988    mode = 'lines'
 989    if name == 'holidays':
 990        
 991        # Combine holidays into one hover text
 992        holidays = m.construct_holiday_dataframe(fcst['ds'])
 993        holiday_features, _, _ = m.make_holiday_features(fcst['ds'], holidays)
 994        holiday_features.columns = holiday_features.columns.str.replace('_delim_', '', regex=False)
 995        holiday_features.columns = holiday_features.columns.str.replace('+0', '', regex=False)
 996        text = pd.Series(data='', index=holiday_features.index)
 997        for holiday_feature, idxs in holiday_features.items():
 998            # https://github.com/facebook/pyrefly/issues/2248
 999            # pyrefly:ignore[unsupported-operation]
1000            text[idxs.astype(bool) & (text != '')] += '<br>'  # Add newline if additional holiday
1001            text[idxs.astype(bool)] += holiday_feature  # pyrefly:ignore[unsupported-operation]
1002
1003    traces = []
1004    traces.append(go.Scatter(
1005        name=name,
1006        x=fcst['ds'],
1007        y=fcst[name],
1008        mode=mode,
1009        line=go.scatter.Line(color=prediction_color, width=line_width),
1010        text=text,
1011    ))
1012    if uncertainty and m.uncertainty_samples and (fcst[name + '_upper'] != fcst[name + '_lower']).any():
1013        if mode == 'markers':
1014            traces[0].update(
1015                error_y=dict(
1016                    type='data',
1017                    symmetric=False,
1018                    array=fcst[name + '_upper'],
1019                    arrayminus=fcst[name + '_lower'],
1020                    width=0,
1021                    color=error_color
1022                )
1023            )
1024        else:
1025            traces.append(go.Scatter(
1026                name=name + '_upper',
1027                x=fcst['ds'],
1028                y=fcst[name + '_upper'],
1029                mode=mode,
1030                line=go.scatter.Line(width=0, color=error_color)
1031            ))
1032            traces.append(go.Scatter(
1033                name=name + '_lower',
1034                x=fcst['ds'],
1035                y=fcst[name + '_lower'],
1036                mode=mode,
1037                line=go.scatter.Line(width=0, color=error_color),
1038                fillcolor=error_color,
1039                fill='tonexty'
1040            ))
1041    if 'cap' in fcst and plot_cap:
1042        traces.append(go.Scatter(
1043            name='Cap',
1044            x=fcst['ds'],
1045            y=fcst['cap'],
1046            mode='lines',
1047            line=go.scatter.Line(color=cap_color, dash='dash', width=line_width),
1048        ))
1049    if m.logistic_floor and 'floor' in fcst and plot_cap:
1050        traces.append(go.Scatter(
1051            name='Floor',
1052            x=fcst['ds'],
1053            y=fcst['floor'],
1054            mode='lines',
1055            line=go.scatter.Line(color=cap_color, dash='dash', width=line_width),
1056        ))
1057
1058    xaxis = go.layout.XAxis(
1059        type='date',
1060        range=range_x)
1061    yaxis = go.layout.YAxis(rangemode='normal' if name == 'trend' else 'tozero',
1062                            title=go.layout.yaxis.Title(text=name),
1063                            zerolinecolor=zeroline_color)
1064    assert m.component_modes
1065    if name in m.component_modes['multiplicative']:
1066        yaxis.update(tickformat='%', hoverformat='.2%')
1067    return {'traces': traces, 'xaxis': xaxis, 'yaxis': yaxis}
1068
1069
1070def get_seasonality_plotly_props(
1071    m: Prophet,
1072    name: str,
1073    uncertainty: bool = True,
1074) -> _PlotlyProps:
1075    """Prepares a dictionary for plotting the selected seasonality with Plotly
1076
1077    Parameters
1078    ----------
1079    m: Prophet model.
1080    name: Name of the component to plot.
1081    uncertainty: Optional boolean to plot uncertainty intervals, which will
1082        only be done if m.uncertainty_samples > 0.
1083
1084    Returns
1085    -------
1086    A dictionary with Plotly traces, xaxis and yaxis
1087    """
1088    prediction_color = '#0072B2'
1089    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
1090    line_width = 2
1091    zeroline_color = '#AAA'
1092
1093    # Compute seasonality from Jan 1 through a single period.
1094    start = pd.to_datetime('2017-01-01 0000')
1095    period = m.seasonalities[name]['period']
1096    end = start + pd.Timedelta(days=period)
1097    assert m.history is not None
1098    if (m.history['ds'].dt.hour == 0).all():  # Day Precision
1099        plot_points = np.floor(period).astype(int)
1100    elif (m.history['ds'].dt.minute == 0).all():  # Hour Precision
1101        plot_points = np.floor(period * 24).astype(int)
1102    else:  # Minute Precision
1103        plot_points = np.floor(period * 24 * 60).astype(int)
1104    days = pd.to_datetime(np.linspace(start.value, end.value, plot_points, endpoint=False))
1105    df_y = seasonality_plot_df(m, days)
1106    seas = m.predict_seasonal_components(df_y)
1107
1108    traces = []
1109    traces.append(go.Scatter(
1110        name=name,
1111        x=df_y['ds'],
1112        y=seas[name],
1113        mode='lines',
1114        line=go.scatter.Line(color=prediction_color, width=line_width)
1115    ))
1116    if uncertainty and m.uncertainty_samples and (seas[name + '_upper'] != seas[name + '_lower']).any():
1117        traces.append(go.Scatter(
1118            name=name + '_upper',
1119            x=df_y['ds'],
1120            y=seas[name + '_upper'],
1121            mode='lines',
1122            line=go.scatter.Line(width=0, color=error_color)
1123        ))
1124        traces.append(go.Scatter(
1125            name=name + '_lower',
1126            x=df_y['ds'],
1127            y=seas[name + '_lower'],
1128            mode='lines',
1129            line=go.scatter.Line(width=0, color=error_color),
1130            fillcolor=error_color,
1131            fill='tonexty'
1132        ))
1133
1134    # Set tick formats (examples are based on 2017-01-06 21:15)
1135    if period <= 2:
1136        tickformat = '%H:%M'  # "21:15"
1137    elif period < 7:
1138        tickformat = '%A %H:%M'  # "Friday 21:15"
1139    elif period < 14:
1140        tickformat = '%A'  # "Friday"
1141    else:
1142        tickformat = '%B %e'  # "January  6"
1143
1144    range_margin = (df_y['ds'].max() - df_y['ds'].min()) * 0.05
1145    xaxis = go.layout.XAxis(
1146        tickformat=tickformat,
1147        type='date',
1148        range=[df_y['ds'].min() - range_margin, df_y['ds'].max() + range_margin]
1149    )
1150
1151    yaxis = go.layout.YAxis(title=go.layout.yaxis.Title(text=name),
1152                            zerolinecolor=zeroline_color)
1153    if m.seasonalities[name]['mode'] == 'multiplicative':
1154        yaxis.update(tickformat='%', hoverformat='.2%')
1155
1156    return {'traces': traces, 'xaxis': xaxis, 'yaxis': yaxis}
logger: logging.Logger = <Logger prophet.plot (INFO)>
def plot( m: 'Prophet', fcst: pandas.DataFrame, ax: matplotlib.axes._axes.Axes | None = None, uncertainty: bool = True, plot_cap: bool = True, xlabel: str = 'ds', ylabel: str = 'y', figsize: tuple[int, int] = (10, 6), include_legend: bool = False) -> matplotlib.figure.Figure:
 61def plot(
 62    m: Prophet,
 63    fcst: pd.DataFrame,
 64    ax: plt.Axes | None = None,
 65    uncertainty: bool = True,
 66    plot_cap: bool = True,
 67    xlabel: str = "ds",
 68    ylabel: str = "y",
 69    figsize: tuple[int, int] = (10, 6),
 70    include_legend: bool = False,
 71) -> plt.Figure:
 72    """Plot the Prophet forecast.
 73
 74    Parameters
 75    ----------
 76    m: Prophet model.
 77    fcst: pd.DataFrame output of m.predict.
 78    ax: Optional matplotlib axes on which to plot.
 79    uncertainty: Optional boolean to plot uncertainty intervals, which will
 80        only be done if m.uncertainty_samples > 0.
 81    plot_cap: Optional boolean indicating if the capacity should be shown
 82        in the figure, if available.
 83    xlabel: Optional label name on X-axis
 84    ylabel: Optional label name on Y-axis
 85    figsize: Optional tuple width, height in inches.
 86    include_legend: Optional boolean to add legend to the plot.
 87
 88    Returns
 89    -------
 90    A matplotlib figure.
 91    """
 92    user_provided_ax = False if ax is None else True
 93    if ax is None:
 94        fig = plt.figure(facecolor='w', figsize=figsize)
 95        ax = fig.add_subplot(111)
 96    else:
 97        fig = cast('plt.Figure', ax.get_figure())
 98    fcst_t = fcst['ds']
 99    history = cast('pd.DataFrame', m.history)
100    ax.plot(history['ds'], history['y'], 'k.', label='Observed data points')
101    ax.plot(fcst_t, fcst['yhat'], ls='-', c='#0072B2', label='Forecast')
102    if 'cap' in fcst and plot_cap:
103        ax.plot(fcst_t, fcst['cap'], ls='--', c='k', label='Maximum capacity')
104    if m.logistic_floor and 'floor' in fcst and plot_cap:
105        ax.plot(fcst_t, fcst['floor'], ls='--', c='k', label='Minimum capacity')
106    if uncertainty and m.uncertainty_samples:
107        ax.fill_between(fcst_t, fcst['yhat_lower'], fcst['yhat_upper'],
108                        color='#0072B2', alpha=0.2, label='Uncertainty interval')
109    # Specify formatting to workaround matplotlib issue #12925
110    locator = AutoDateLocator(interval_multiples=False)
111    formatter = AutoDateFormatter(locator)
112    ax.xaxis.set_major_locator(locator)
113    ax.xaxis.set_major_formatter(formatter)
114    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
115    ax.set_xlabel(xlabel)
116    ax.set_ylabel(ylabel)
117    if include_legend:
118        ax.legend()
119    if not user_provided_ax:
120        try:
121            if fig.get_layout_engine() is None:
122                fig.tight_layout()
123        except AttributeError:
124            fig.tight_layout()
125    return fig

Plot the Prophet forecast.

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • ax (Optional matplotlib axes on which to plot.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
  • xlabel (Optional label name on X-axis):

  • ylabel (Optional label name on Y-axis):

  • figsize (Optional tuple width, height in inches.):

  • include_legend (Optional boolean to add legend to the plot.):

Returns
  • A matplotlib figure.
def plot_components( m: 'Prophet', fcst: pandas.DataFrame, uncertainty: bool = True, plot_cap: bool = True, weekly_start: int = 0, yearly_start: int = 0, figsize: tuple[int, int] | None = None) -> matplotlib.figure.Figure:
128def plot_components(
129    m: Prophet,
130    fcst: pd.DataFrame,
131    uncertainty: bool = True,
132    plot_cap: bool = True,
133    weekly_start: int = 0,
134    yearly_start: int = 0,
135    figsize: tuple[int, int] | None = None,
136) -> plt.Figure:
137    """Plot the Prophet forecast components.
138
139    Will plot whichever are available of: trend, holidays, weekly
140    seasonality, yearly seasonality, and additive and multiplicative extra
141    regressors.
142
143    Parameters
144    ----------
145    m: Prophet model.
146    fcst: pd.DataFrame output of m.predict.
147    uncertainty: Optional boolean to plot uncertainty intervals, which will
148        only be done if m.uncertainty_samples > 0.
149    plot_cap: Optional boolean indicating if the capacity should be shown
150        in the figure, if available.
151    weekly_start: Optional int specifying the start day of the weekly
152        seasonality plot. 0 (default) starts the week on Sunday. 1 shifts
153        by 1 day to Monday, and so on.
154    yearly_start: Optional int specifying the start day of the yearly
155        seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts
156        by 1 day to Jan 2, and so on.
157    figsize: Optional tuple width, height in inches.
158
159    Returns
160    -------
161    A matplotlib figure.
162    """
163    # Identify components to be plotted
164    components = ['trend']
165    if m.train_holiday_names is not None and 'holidays' in fcst:
166        components.append('holidays')
167    # Plot weekly seasonality, if present
168    if 'weekly' in m.seasonalities and 'weekly' in fcst:
169        components.append('weekly')
170    # Yearly if present
171    if 'yearly' in m.seasonalities and 'yearly' in fcst:
172        components.append('yearly')
173    # Other seasonalities
174    components.extend([
175        name for name in sorted(m.seasonalities)
176        if name in fcst and name not in ['weekly', 'yearly']
177    ])
178    regressors = {'additive': False, 'multiplicative': False}
179    for name, props in m.extra_regressors.items():
180        regressors[props['mode']] = True
181    for mode in ['additive', 'multiplicative']:
182        if regressors[mode] and 'extra_regressors_{}'.format(mode) in fcst:
183            components.append('extra_regressors_{}'.format(mode))
184    npanel = len(components)
185
186    figsize = figsize if figsize else (9, 3 * npanel)
187    fig, axes = plt.subplots(npanel, 1, facecolor='w', figsize=figsize)
188
189    if npanel == 1:
190        axes = [axes]
191
192    multiplicative_axes = []
193
194    dt = cast('pd.DataFrame', m.history)['ds'].diff()
195    min_dt = dt.iloc[cast('np.ndarray', dt.values).nonzero()[0]].min() 
196
197    for ax, plot_name in zip(axes, components):
198        if plot_name == 'trend':
199            plot_forecast_component(
200                m=m, fcst=fcst, name='trend', ax=ax, uncertainty=uncertainty,
201                plot_cap=plot_cap,
202            )
203        elif plot_name in m.seasonalities:
204            if (
205                (plot_name == 'weekly' or m.seasonalities[plot_name]['period'] == 7)
206                and (min_dt == pd.Timedelta(days=1))
207            ):
208                plot_weekly(
209                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty, weekly_start=weekly_start
210                )
211            elif plot_name == 'yearly' or m.seasonalities[plot_name]['period'] == 365.25:
212                plot_yearly(
213                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty, yearly_start=yearly_start
214                )
215            else:
216                plot_seasonality(
217                    m=m, name=plot_name, ax=ax, uncertainty=uncertainty,
218                )
219        elif plot_name in [
220            'holidays',
221            'extra_regressors_additive',
222            'extra_regressors_multiplicative',
223        ]:
224            plot_forecast_component(
225                m=m, fcst=fcst, name=plot_name, ax=ax, uncertainty=uncertainty,
226                plot_cap=False,
227            )
228        assert m.component_modes is not None
229        if plot_name in m.component_modes['multiplicative']:
230            multiplicative_axes.append(ax)
231
232    try:
233        if fig.get_layout_engine() is None:
234            fig.tight_layout()
235    except AttributeError:
236        fig.tight_layout()
237    # Reset multiplicative axes labels after tight_layout adjustment
238    for ax in multiplicative_axes:
239        ax = set_y_as_percent(ax)
240    return fig

Plot the Prophet forecast components.

Will plot whichever are available of: trend, holidays, weekly seasonality, yearly seasonality, and additive and multiplicative extra regressors.

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
  • weekly_start (Optional int 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.
  • yearly_start (Optional int 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.
  • figsize (Optional tuple width, height in inches.):
Returns
  • A matplotlib figure.
def plot_forecast_component( m: 'Prophet', fcst: pandas.DataFrame, name: str, ax: matplotlib.axes._axes.Axes | None = None, uncertainty: bool = True, plot_cap: bool = False, figsize: tuple[int, int] = (10, 6)) -> Sequence[matplotlib.artist.Artist]:
243def plot_forecast_component(
244    m: Prophet,
245    fcst: pd.DataFrame,
246    name: str,
247    ax: plt.Axes | None = None,
248    uncertainty: bool = True,
249    plot_cap: bool = False,
250    figsize: tuple[int, int] = (10, 6),
251) -> Sequence[plt.Artist]:
252    """Plot a particular component of the forecast.
253
254    Parameters
255    ----------
256    m: Prophet model.
257    fcst: pd.DataFrame output of m.predict.
258    name: Name of the component to plot.
259    ax: Optional matplotlib Axes to plot on.
260    uncertainty: Optional boolean to plot uncertainty intervals, which will
261        only be done if m.uncertainty_samples > 0.
262    plot_cap: Optional boolean indicating if the capacity should be shown
263        in the figure, if available.
264    figsize: Optional tuple width, height in inches.
265
266    Returns
267    -------
268    a list of matplotlib artists
269    """
270    artists = []
271    if not ax:
272        fig = plt.figure(facecolor='w', figsize=figsize)
273        ax = fig.add_subplot(111)
274    fcst_t = fcst['ds']
275    artists += ax.plot(fcst_t, fcst[name], ls='-', c='#0072B2')
276    if 'cap' in fcst and plot_cap:
277        artists += ax.plot(fcst_t, fcst['cap'], ls='--', c='k')
278    if m.logistic_floor and 'floor' in fcst and plot_cap:
279        ax.plot(fcst_t, fcst['floor'], ls='--', c='k')
280    if uncertainty and m.uncertainty_samples:
281        artists += [ax.fill_between(
282            fcst_t, fcst[name + '_lower'], fcst[name + '_upper'],
283            color='#0072B2', alpha=0.2)]
284    # Specify formatting to workaround matplotlib issue #12925
285    locator = AutoDateLocator(interval_multiples=False)
286    formatter = AutoDateFormatter(locator)
287    ax.xaxis.set_major_locator(locator)
288    ax.xaxis.set_major_formatter(formatter)
289    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
290    ax.set_xlabel('ds')
291    ax.set_ylabel(name)
292    assert m.component_modes
293    if name in m.component_modes['multiplicative']:
294        ax = set_y_as_percent(ax)
295    return artists

Plot a particular component of the forecast.

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • name (Name of the component to plot.):

  • ax (Optional matplotlib Axes to plot on.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
  • figsize (Optional tuple width, height in inches.):
Returns
  • a list of matplotlib artists
def seasonality_plot_df( m: 'Prophet', ds: Union[Sequence[pandas.Timestamp], pandas.DatetimeIndex]) -> pandas.DataFrame:
298def seasonality_plot_df(
299    m: Prophet,
300    ds: Sequence[pd.Timestamp] | pd.DatetimeIndex,
301) -> pd.DataFrame:
302    """Prepare dataframe for plotting seasonal components.
303
304    Parameters
305    ----------
306    m: Prophet model.
307    ds: List of dates for column ds.
308
309    Returns
310    -------
311    A dataframe with seasonal components on ds.
312    """
313    df_dict = {'ds': ds, 'cap': 1., 'floor': 0.}
314    for name in m.extra_regressors:
315        df_dict[name] = 0.
316    # Activate all conditional seasonality columns
317    for props in m.seasonalities.values():
318        if props['condition_name'] is not None:
319            df_dict[props['condition_name']] = True
320    df = pd.DataFrame(df_dict)
321    df = m.setup_dataframe(df)
322    return df

Prepare dataframe for plotting seasonal components.

Parameters
  • m (Prophet model.):

  • ds (List of dates for column ds.):

Returns
  • A dataframe with seasonal components on ds.
def plot_weekly( m: 'Prophet', ax: matplotlib.axes._axes.Axes | None = None, uncertainty: bool = True, weekly_start: int = 0, figsize: tuple[int, int] = (10, 6), name: str = 'weekly') -> Sequence[matplotlib.artist.Artist]:
325def plot_weekly(
326    m: Prophet,
327    ax: plt.Axes | None = None,
328    uncertainty: bool = True,
329    weekly_start: int = 0,
330    figsize: tuple[int, int] = (10, 6),
331    name: str = 'weekly',
332) -> Sequence[plt.Artist]:
333    """Plot the weekly component of the forecast.
334
335    Parameters
336    ----------
337    m: Prophet model.
338    ax: Optional matplotlib Axes to plot on. One will be created if this
339        is not provided.
340    uncertainty: Optional boolean to plot uncertainty intervals, which will
341        only be done if m.uncertainty_samples > 0.
342    weekly_start: Optional int specifying the start day of the weekly
343        seasonality plot. 0 (default) starts the week on Sunday. 1 shifts
344        by 1 day to Monday, and so on.
345    figsize: Optional tuple width, height in inches.
346    name: Name of seasonality component if changed from default 'weekly'.
347
348    Returns
349    -------
350    a list of matplotlib artists
351    """
352    artists = []
353    if not ax:
354        fig = plt.figure(facecolor='w', figsize=figsize)
355        ax = fig.add_subplot(111)
356    # Compute weekly seasonality for a Sun-Sat sequence of dates.
357    days = (pd.date_range(start='2017-01-01', periods=7) +
358            pd.Timedelta(days=weekly_start))
359    df_w = seasonality_plot_df(m, days)
360    seas = m.predict_seasonal_components(df_w)
361    days = days.day_name()
362    artists += ax.plot(range(len(days)), seas[name], ls='-',
363                    c='#0072B2')
364    if uncertainty and m.uncertainty_samples:
365        artists += [ax.fill_between(range(len(days)),
366                                    seas[name + '_lower'], seas[name + '_upper'],
367                                    color='#0072B2', alpha=0.2)]
368    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
369    ax.set_xticks(range(len(days)))
370    ax.set_xticklabels(days)
371    ax.set_xlabel('Day of week')
372    ax.set_ylabel(name)
373    if m.seasonalities[name]['mode'] == 'multiplicative':
374        ax = set_y_as_percent(ax)
375    return artists

Plot the weekly component of the forecast.

Parameters
  • m (Prophet model.):

  • ax (Optional matplotlib Axes to plot on. One will be created if this): is not provided.

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.
  • weekly_start (Optional int 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.
  • figsize (Optional tuple width, height in inches.):

  • name (Name of seasonality component if changed from default 'weekly'.):

Returns
  • a list of matplotlib artists
def plot_yearly( m: 'Prophet', ax: matplotlib.axes._axes.Axes | None = None, uncertainty: bool = True, yearly_start: int = 0, figsize: tuple[int, int] = (10, 6), name: str = 'yearly') -> Sequence[matplotlib.artist.Artist]:
378def plot_yearly(
379    m: Prophet,
380    ax: plt.Axes | None = None,
381    uncertainty: bool = True,
382    yearly_start: int = 0,
383    figsize: tuple[int, int] = (10, 6),
384    name: str = 'yearly',
385) -> Sequence[plt.Artist]:
386    """Plot the yearly component of the forecast.
387
388    Parameters
389    ----------
390    m: Prophet model.
391    ax: Optional matplotlib Axes to plot on. One will be created if
392        this is not provided.
393    uncertainty: Optional boolean to plot uncertainty intervals, which will
394        only be done if m.uncertainty_samples > 0.
395    yearly_start: Optional int specifying the start day of the yearly
396        seasonality plot. 0 (default) starts the year on Jan 1. 1 shifts
397        by 1 day to Jan 2, and so on.
398    figsize: Optional tuple width, height in inches.
399    name: Name of seasonality component if previously changed from default 'yearly'.
400
401    Returns
402    -------
403    a list of matplotlib artists
404    """
405    artists = []
406    if not ax:
407        fig = plt.figure(facecolor='w', figsize=figsize)
408        ax = fig.add_subplot(111)
409    # Compute yearly seasonality for a Jan 1 - Dec 31 sequence of dates.
410    days = (pd.date_range(start='2017-01-01', periods=365) +
411            pd.Timedelta(days=yearly_start))
412    df_y = seasonality_plot_df(m, days)
413    seas = m.predict_seasonal_components(df_y)
414    artists += ax.plot(
415        df_y['ds'], seas[name], ls='-', c='#0072B2')
416    if uncertainty and m.uncertainty_samples:
417        artists += [ax.fill_between(
418            df_y['ds'], seas[name + '_lower'],
419            seas[name + '_upper'], color='#0072B2', alpha=0.2)]
420    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
421    months = MonthLocator(range(1, 13), bymonthday=1, interval=2)
422    ax.xaxis.set_major_formatter(FuncFormatter(
423        lambda x, pos=None: '{dt:%B} {dt.day}'.format(dt=num2date(x))))
424    ax.xaxis.set_major_locator(months)
425    ax.set_xlabel('Day of year')
426    ax.set_ylabel(name)
427    if m.seasonalities[name]['mode'] == 'multiplicative':
428        ax = set_y_as_percent(ax)
429    return artists

Plot the yearly component of the forecast.

Parameters
  • m (Prophet model.):

  • ax (Optional matplotlib Axes to plot on. One will be created if): this is not provided.

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.
  • yearly_start (Optional int 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.
  • figsize (Optional tuple width, height in inches.):

  • name (Name of seasonality component if previously changed from default 'yearly'.):

Returns
  • a list of matplotlib artists
def plot_seasonality( m: 'Prophet', name: str, ax: matplotlib.axes._axes.Axes | None = None, uncertainty: bool = True, figsize: tuple[int, int] = (10, 6)) -> Sequence[matplotlib.artist.Artist]:
432def plot_seasonality(
433    m: Prophet,
434    name: str,
435    ax: plt.Axes | None = None,
436    uncertainty: bool = True,
437    figsize: tuple[int, int] = (10, 6),
438) -> Sequence[plt.Artist]:
439    """Plot a custom seasonal component.
440
441    Parameters
442    ----------
443    m: Prophet model.
444    name: Seasonality name, like 'daily', 'weekly'.
445    ax: Optional matplotlib Axes to plot on. One will be created if
446        this is not provided.
447    uncertainty: Optional boolean to plot uncertainty intervals, which will
448        only be done if m.uncertainty_samples > 0.
449    figsize: Optional tuple width, height in inches.
450
451    Returns
452    -------
453    a list of matplotlib artists
454    """
455    artists = []
456    if not ax:
457        fig = plt.figure(facecolor='w', figsize=figsize)
458        ax = fig.add_subplot(111)
459    # Compute seasonality from Jan 1 through a single period.
460    start = pd.to_datetime('2017-01-01 0000')
461    period = m.seasonalities[name]['period']
462    end = start + pd.Timedelta(days=period)
463    plot_points = 200
464    # https://github.com/pandas-dev/pandas-stubs/issues/1645
465    days = pd.to_datetime(np.linspace(start.value, end.value, plot_points))  # pyrefly:ignore[no-matching-overload]
466    df_y = seasonality_plot_df(m, days)
467    seas = m.predict_seasonal_components(df_y)
468    artists += ax.plot(df_y['ds'], seas[name], ls='-',
469                        c='#0072B2')
470    if uncertainty and m.uncertainty_samples:
471        artists += [ax.fill_between(
472            df_y['ds'], seas[name + '_lower'],
473            seas[name + '_upper'], color='#0072B2', alpha=0.2)]
474    ax.grid(True, which='major', c='gray', ls='-', lw=1, alpha=0.2)
475    n_ticks = 8
476    # https://github.com/pandas-dev/pandas-stubs/issues/1645
477    xticks = pd.to_datetime(np.linspace(start.value, end.value, n_ticks)  # pyrefly:ignore[no-matching-overload]
478        ).to_pydatetime()
479    ax.set_xticks(xticks)
480    if name == 'yearly':
481        fmt = FuncFormatter(
482            lambda x, pos=None: '{dt:%B} {dt.day}'.format(dt=num2date(x)))
483        ax.set_xlabel('Day of year')
484    elif name == 'weekly':
485        fmt = FuncFormatter(
486            lambda x, pos=None: '{dt:%A}'.format(dt=num2date(x)))
487        ax.set_xlabel('Day of Week')
488    elif name == 'daily':
489        fmt = FuncFormatter(
490            lambda x, pos=None: '{dt:%T}'.format(dt=num2date(x)))
491        ax.set_xlabel('Hour of day')
492    elif period <= 2:
493        fmt = FuncFormatter(
494            lambda x, pos=None: '{dt:%T}'.format(dt=num2date(x)))
495        ax.set_xlabel('Hours')
496    else:
497        fmt = FuncFormatter(
498            lambda x, pos=None: '{:.0f}'.format(pos * period / (n_ticks - 1)))
499        ax.set_xlabel('Days')
500    ax.xaxis.set_major_formatter(fmt)
501    ax.set_ylabel(name)
502    if m.seasonalities[name]['mode'] == 'multiplicative':
503        ax = set_y_as_percent(ax)
504    return artists

Plot a custom seasonal component.

Parameters
  • m (Prophet model.):

  • name (Seasonality name, like 'daily', 'weekly'.):

  • ax (Optional matplotlib Axes to plot on. One will be created if): this is not provided.

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.
  • figsize (Optional tuple width, height in inches.):
Returns
  • a list of matplotlib artists
def set_y_as_percent(ax: '_AxT') -> '_AxT':
507def set_y_as_percent(ax: _AxT) -> _AxT:
508    yticks = 100 * ax.get_yticks()
509    yticklabels = ['{0:.4g}%'.format(y) for y in yticks]
510    ax.set_yticks(ax.get_yticks().tolist())
511    ax.set_yticklabels(yticklabels)
512    return ax
def add_changepoints_to_plot( ax: matplotlib.axes._axes.Axes, m: 'Prophet', fcst: pandas.DataFrame, threshold: float = 0.01, cp_color: str = 'r', cp_linestyle: str = '--', trend: bool = True) -> list[matplotlib.lines.Line2D]:
515def add_changepoints_to_plot(
516    ax: plt.Axes,
517    m: Prophet,
518    fcst: pd.DataFrame,
519    threshold: float = 0.01,
520    cp_color: str = 'r',
521    cp_linestyle: str = '--',
522    trend: bool = True,
523) -> list[plt.Line2D]:
524    """Add markers for significant changepoints to prophet forecast plot.
525
526    Example:
527    fig = m.plot(forecast)
528    add_changepoints_to_plot(fig.gca(), m, forecast)
529
530    Parameters
531    ----------
532    ax: axis on which to overlay changepoint markers.
533    m: Prophet model.
534    fcst: Forecast output from m.predict.
535    threshold: Threshold on trend change magnitude for significance.
536    cp_color: Color of changepoint markers.
537    cp_linestyle: Linestyle for changepoint markers.
538    trend: If True, will also overlay the trend.
539
540    Returns
541    -------
542    a list of matplotlib artists
543    """
544    artists = []
545    if trend:
546        artists.extend(ax.plot(fcst['ds'], fcst['trend'], c=cp_color))
547    
548    assert m.changepoints is not None
549    signif_changepoints = m.changepoints[
550        np.abs(np.nanmean(m.params['delta'], axis=0)) >= threshold
551    ] if len(m.changepoints) > 0 else []
552    for cp in signif_changepoints:
553        # Matplotlib stubs type axvline x as float; pandas Timestamp is accepted at runtime.
554        artists.append(ax.axvline(x=cp, c=cp_color, ls=cp_linestyle))  # pyrefly:ignore[bad-argument-type]
555    return artists

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
def plot_cross_validation_metric( df_cv: pandas.DataFrame, metric: str, rolling_window: float = 0.1, ax: matplotlib.axes._axes.Axes | None = None, figsize: tuple[int, int] = (10, 6), color: str = 'b', point_color: str = 'gray') -> matplotlib.figure.Figure:
558def plot_cross_validation_metric(
559    df_cv: pd.DataFrame,
560    metric: str,
561    rolling_window: float = 0.1,
562    ax: plt.Axes | None = None,
563    figsize: tuple[int, int] = (10, 6),
564    color: str = 'b',
565    point_color: str = 'gray',
566) -> plt.Figure:
567    """Plot a performance metric vs. forecast horizon from cross validation.
568
569    Cross validation produces a collection of out-of-sample model predictions
570    that can be compared to actual values, at a range of different horizons
571    (distance from the cutoff). This computes a specified performance metric
572    for each prediction, and aggregated over a rolling window with horizon.
573
574    This uses prophet.diagnostics.performance_metrics to compute the metrics.
575    Valid values of metric are 'mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', and 'coverage'.
576
577    rolling_window is the proportion of data included in the rolling window of
578    aggregation. The default value of 0.1 means 10% of data are included in the
579    aggregation for computing the metric.
580
581    As a concrete example, if metric='mse', then this plot will show the
582    squared error for each cross validation prediction, along with the MSE
583    averaged over rolling windows of 10% of the data.
584
585    Parameters
586    ----------
587    df_cv: The output from prophet.diagnostics.cross_validation.
588    metric: Metric name, one of ['mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', 'coverage'].
589    rolling_window: Proportion of data to use for rolling average of metric.
590        In [0, 1]. Defaults to 0.1.
591    ax: Optional matplotlib axis on which to plot. If not given, a new figure
592        will be created.
593    figsize: Optional tuple width, height in inches.
594    color: Optional color for plot and error points, useful when plotting
595        multiple model performances on one axis for comparison.
596
597    Returns
598    -------
599    a matplotlib figure.
600    """
601    if ax is None:
602        fig = plt.figure(facecolor='w', figsize=figsize)
603        ax = fig.add_subplot(111)
604    else:
605        fig = cast('plt.Figure', ax.get_figure())
606    # Get the metric at the level of individual predictions, and with the rolling window.
607    df_none = performance_metrics(df_cv, metrics=[metric], rolling_window=-1)
608    df_h = performance_metrics(df_cv, metrics=[metric], rolling_window=rolling_window)
609    
610    assert df_none is not None
611    assert df_h is not None
612
613    # Some work because matplotlib does not handle timedelta
614    # Target ~10 ticks.
615    tick_w = max(df_none['horizon'].astype('timedelta64[ns]')) / 10.
616    # Find the largest time resolution that has <1 unit per bin.
617    dts: list[Literal["D", "h", "m", "s", "ms", "us", "ns"]]
618    dts = ['D', 'h', 'm', 's', 'ms', 'us', 'ns']
619    dt_names = [
620        'days', 'hours', 'minutes', 'seconds', 'milliseconds', 'microseconds',
621        'nanoseconds'
622    ]
623    dt_conversions = [
624        24 * 60 * 60 * 10 ** 9,
625        60 * 60 * 10 ** 9,
626        60 * 10 ** 9,
627        10 ** 9,
628        10 ** 6,
629        10 ** 3,
630        1.,
631    ]
632    for i, dt in enumerate(dts):
633        if np.timedelta64(1, dt) < np.timedelta64(tick_w, 'ns'):
634            break
635
636    x_plt = np.asarray(df_none['horizon'].astype('timedelta64[ns]')).view(np.int64) / float(dt_conversions[i])
637    x_plt_h = np.asarray(df_h['horizon'].astype('timedelta64[ns]')).view(np.int64) / float(dt_conversions[i])
638
639    ax.plot(x_plt, df_none[metric], '.', alpha=0.1, c=point_color)
640    ax.plot(x_plt_h, df_h[metric], '-', c=color)
641    ax.grid(True)
642
643    ax.set_xlabel('Horizon ({})'.format(dt_names[i]))
644    ax.set_ylabel(metric)
645    return fig

Plot a performance metric vs. forecast horizon from cross validation.

Cross validation produces a collection of out-of-sample model predictions that can be compared to actual values, at a range of different horizons (distance from the cutoff). This computes a specified performance metric for each prediction, and aggregated over a rolling window with horizon.

This uses prophet.diagnostics.performance_metrics to compute the metrics. Valid values of metric are 'mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', and 'coverage'.

rolling_window is the proportion of data included in the rolling window of aggregation. The default value of 0.1 means 10% of data are included in the aggregation for computing the metric.

As a concrete example, if metric='mse', then this plot will show the squared error for each cross validation prediction, along with the MSE averaged over rolling windows of 10% of the data.

Parameters
  • df_cv (The output from prophet.diagnostics.cross_validation.):

  • metric (Metric name, one of ['mse', 'rmse', 'mae', 'mape', 'mdape', 'smape', 'coverage'].):

  • rolling_window (Proportion of data to use for rolling average of metric.): In [0, 1]. Defaults to 0.1.

  • ax (Optional matplotlib axis on which to plot. If not given, a new figure): will be created.
  • figsize (Optional tuple width, height in inches.):

  • color (Optional color for plot and error points, useful when plotting): multiple model performances on one axis for comparison.

Returns
  • a matplotlib figure.
def plot_plotly( m: 'Prophet', fcst: pandas.DataFrame, uncertainty: bool = True, plot_cap: bool = True, trend: bool = False, changepoints: bool = False, changepoints_threshold: float = 0.01, xlabel: str = 'ds', ylabel: str = 'y', figsize: tuple[int, int] = (900, 600)) -> plotly.graph_objs._figure.Figure:
648def plot_plotly(
649    m: Prophet,
650    fcst: pd.DataFrame,
651    uncertainty: bool = True,
652    plot_cap: bool = True,
653    trend: bool = False,
654    changepoints: bool = False,
655    changepoints_threshold: float = 0.01,
656    xlabel: str = 'ds',
657    ylabel: str = 'y',
658    figsize: tuple[int, int] = (900, 600)
659) -> go.Figure:
660    """Plot the Prophet forecast with Plotly offline.
661
662    Plotting in Jupyter Notebook requires initializing plotly.offline.init_notebook_mode():
663    >>> import plotly.offline as py
664    >>> py.init_notebook_mode()
665    Then the figure can be displayed using plotly.offline.iplot(...):
666    >>> fig = plot_plotly(m, fcst)
667    >>> py.iplot(fig)
668    see https://plot.ly/python/offline/ for details
669
670    Parameters
671    ----------
672    m: Prophet model.
673    fcst: pd.DataFrame output of m.predict.
674    uncertainty: Optional boolean to plot uncertainty intervals.
675    plot_cap: Optional boolean indicating if the capacity should be shown
676        in the figure, if available.
677    trend: Optional boolean to plot trend
678    changepoints: Optional boolean to plot changepoints
679    changepoints_threshold: Threshold on trend change magnitude for significance.
680    xlabel: Optional label name on X-axis
681    ylabel: Optional label name on Y-axis
682    figsize: The plot's size (in px).
683
684    Returns
685    -------
686    A Plotly Figure.
687    """
688    prediction_color = '#0072B2'
689    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
690    actual_color = 'black'
691    cap_color = 'black'
692    trend_color = '#B23B00'
693    line_width = 2
694    marker_size = 4
695
696    data = []
697    # Add actual
698    assert m.history
699    data.append(go.Scatter(
700        name='Actual',
701        x=m.history['ds'],
702        y=m.history['y'],
703        marker=dict(color=actual_color, size=marker_size),
704        mode='markers'
705    ))
706    # Add lower bound
707    if uncertainty and m.uncertainty_samples:
708        data.append(go.Scatter(
709            x=fcst['ds'],
710            y=fcst['yhat_lower'],
711            mode='lines',
712            line=dict(width=0),
713            hoverinfo='skip'
714        ))
715    # Add prediction
716    data.append(go.Scatter(
717        name='Predicted',
718        x=fcst['ds'],
719        y=fcst['yhat'],
720        mode='lines',
721        line=dict(color=prediction_color, width=line_width),
722        fillcolor=error_color,
723        fill='tonexty' if uncertainty and m.uncertainty_samples else 'none'
724    ))
725    # Add upper bound
726    if uncertainty and m.uncertainty_samples:
727        data.append(go.Scatter(
728            x=fcst['ds'],
729            y=fcst['yhat_upper'],
730            mode='lines',
731            line=dict(width=0),
732            fillcolor=error_color,
733            fill='tonexty',
734            hoverinfo='skip'
735        ))
736    # Add caps
737    if 'cap' in fcst and plot_cap:
738        data.append(go.Scatter(
739            name='Cap',
740            x=fcst['ds'],
741            y=fcst['cap'],
742            mode='lines',
743            line=dict(color=cap_color, dash='dash', width=line_width),
744        ))
745    if m.logistic_floor and 'floor' in fcst and plot_cap:
746        data.append(go.Scatter(
747            name='Floor',
748            x=fcst['ds'],
749            y=fcst['floor'],
750            mode='lines',
751            line=dict(color=cap_color, dash='dash', width=line_width),
752        ))
753    # Add trend
754    if trend:
755        data.append(go.Scatter(
756            name='Trend',
757            x=fcst['ds'],
758            y=fcst['trend'],
759            mode='lines',
760            line=dict(color=trend_color, width=line_width),
761        ))
762    # Add changepoints
763    assert m.changepoints
764    if changepoints and len(m.changepoints) > 0:
765        signif_changepoints = m.changepoints[
766            np.abs(np.nanmean(m.params['delta'], axis=0)) >= changepoints_threshold
767        ]
768        data.append(go.Scatter(
769            x=signif_changepoints,
770            y=fcst.loc[fcst['ds'].isin(signif_changepoints), 'trend'],
771            marker=dict(size=50, symbol='line-ns-open', color=trend_color,
772                        line=dict(width=line_width)),
773            mode='markers',
774            hoverinfo='skip'
775        ))
776
777    layout = dict(
778        showlegend=False,
779        width=figsize[0],
780        height=figsize[1],
781        yaxis=dict(
782            title=ylabel
783        ),
784        xaxis=dict(
785            title=xlabel,
786            type='date',
787            rangeselector=dict(
788                buttons=list([
789                    dict(count=7,
790                         label='1w',
791                         step='day',
792                         stepmode='backward'),
793                    dict(count=1,
794                         label='1m',
795                         step='month',
796                         stepmode='backward'),
797                    dict(count=6,
798                         label='6m',
799                         step='month',
800                         stepmode='backward'),
801                    dict(count=1,
802                         label='1y',
803                         step='year',
804                         stepmode='backward'),
805                    dict(step='all')
806                ])
807            ),
808            rangeslider=dict(
809                visible=True
810            ),
811        ),
812    )
813    fig = go.Figure(data=data, layout=layout)
814    return fig

Plot the Prophet forecast with Plotly offline.

Plotting in Jupyter Notebook requires initializing plotly.offline.init_notebook_mode():

>>> import plotly.offline as py
>>> py.init_notebook_mode()
Then the figure can be displayed using plotly.offline.iplot(...):
>>> fig = plot_plotly(m, fcst)
>>> py.iplot(fig)
see https://plot.ly/python/offline/ for details
Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • uncertainty (Optional boolean to plot uncertainty intervals.):

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.

  • trend (Optional boolean to plot trend):

  • changepoints (Optional boolean to plot changepoints):

  • changepoints_threshold (Threshold on trend change magnitude for significance.):

  • xlabel (Optional label name on X-axis):

  • ylabel (Optional label name on Y-axis):

  • figsize (The plot's size (in px).):

Returns
  • A Plotly Figure.
def plot_components_plotly( m: 'Prophet', fcst: pandas.DataFrame, uncertainty: bool = True, plot_cap: bool = True, figsize: tuple[int, int] = (900, 200)) -> plotly.graph_objs._figure.Figure:
817def plot_components_plotly(
818    m: Prophet,
819    fcst: pd.DataFrame,
820    uncertainty: bool = True,
821    plot_cap: bool = True,
822    figsize: tuple[int, int] = (900, 200),
823) -> go.Figure:
824    """Plot the Prophet forecast components using Plotly.
825    See plot_plotly() for Plotly setup instructions
826
827    Will plot whichever are available of: trend, holidays, weekly
828    seasonality, yearly seasonality, and additive and multiplicative extra
829    regressors.
830
831    Parameters
832    ----------
833    m: Prophet model.
834    fcst: pd.DataFrame output of m.predict.
835    uncertainty: Optional boolean to plot uncertainty intervals, which will
836        only be done if m.uncertainty_samples > 0.
837    plot_cap: Optional boolean indicating if the capacity should be shown
838        in the figure, if available.
839    figsize: Set the size for the subplots (in px).
840
841    Returns
842    -------
843    A Plotly Figure.
844    """
845
846    # Identify components to plot and get their Plotly props
847    components = {}
848    components['trend'] = get_forecast_component_plotly_props(
849        m, fcst, 'trend', uncertainty, plot_cap)
850    if m.train_holiday_names is not None and 'holidays' in fcst:
851        components['holidays'] = get_forecast_component_plotly_props(
852            m, fcst, 'holidays', uncertainty)
853
854    regressors = {'additive': False, 'multiplicative': False}
855    for name, props in m.extra_regressors.items():
856        regressors[props['mode']] = True
857    for mode in ['additive', 'multiplicative']:
858        if regressors[mode] and 'extra_regressors_{}'.format(mode) in fcst:
859            components['extra_regressors_{}'.format(mode)] = get_forecast_component_plotly_props(
860                m, fcst, 'extra_regressors_{}'.format(mode))
861    for seasonality in m.seasonalities:
862        components[seasonality] = get_seasonality_plotly_props(m, seasonality)
863
864    # Create Plotly subplot figure and add the components to it
865    fig = make_subplots(rows=len(components), cols=1, print_grid=False)
866    fig['layout'].update(go.Layout(
867        showlegend=False,
868        width=figsize[0],
869        height=figsize[1] * len(components)
870    ))
871    for i, name in enumerate(components):
872        if i == 0:
873            xaxis = fig['layout']['xaxis']
874            yaxis = fig['layout']['yaxis']
875        else:
876            xaxis = fig['layout']['xaxis{}'.format(i + 1)]
877            yaxis = fig['layout']['yaxis{}'.format(i + 1)]
878        xaxis.update(components[name]['xaxis'])
879        yaxis.update(components[name]['yaxis'])
880        for trace in components[name]['traces']:
881            fig.append_trace(trace, i + 1, 1)
882    return fig

Plot the Prophet forecast components using Plotly. See plot_plotly() for Plotly setup instructions

Will plot whichever are available of: trend, holidays, weekly seasonality, yearly seasonality, and additive and multiplicative extra regressors.

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
  • figsize (Set the size for the subplots (in px).):
Returns
  • A Plotly Figure.
def plot_forecast_component_plotly( m: 'Prophet', fcst: pandas.DataFrame, name: str, uncertainty: bool = True, plot_cap: bool = False, figsize: tuple[int, int] = (900, 300)) -> plotly.graph_objs._figure.Figure:
885def plot_forecast_component_plotly(
886    m: Prophet,
887    fcst: pd.DataFrame,
888    name: str,
889    uncertainty: bool = True,
890    plot_cap: bool = False,
891    figsize: tuple[int, int] = (900, 300)
892) -> go.Figure:
893    """Plot a particular component of the forecast using Plotly.
894    See plot_plotly() for Plotly setup instructions
895
896    Parameters
897    ----------
898    m: Prophet model.
899    fcst: pd.DataFrame output of m.predict.
900    name: Name of the component to plot.
901    uncertainty: Optional boolean to plot uncertainty intervals, which will
902        only be done if m.uncertainty_samples > 0.
903    plot_cap: Optional boolean indicating if the capacity should be shown
904        in the figure, if available.
905    figsize: The plot's size (in px).
906
907    Returns
908    -------
909    A Plotly Figure.
910    """
911    props = get_forecast_component_plotly_props(m, fcst, name, uncertainty, plot_cap)
912    layout = go.Layout(
913        width=figsize[0],
914        height=figsize[1],
915        showlegend=False,
916        xaxis=props['xaxis'],
917        yaxis=props['yaxis']
918    )
919    fig = go.Figure(data=props['traces'], layout=layout)
920    return fig

Plot a particular component of the forecast using Plotly. See plot_plotly() for Plotly setup instructions

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • name (Name of the component to plot.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
  • figsize (The plot's size (in px).):
Returns
  • A Plotly Figure.
def plot_seasonality_plotly( m: 'Prophet', name: str, uncertainty: bool = True, figsize: tuple[int, int] = (900, 300)) -> plotly.graph_objs._figure.Figure:
923def plot_seasonality_plotly(
924    m: Prophet,
925    name: str,
926    uncertainty: bool = True,
927    figsize: tuple[int, int] = (900, 300)
928) -> go.Figure:
929    """Plot a custom seasonal component using Plotly.
930    See plot_plotly() for Plotly setup instructions
931
932    Parameters
933    ----------
934    m: Prophet model.
935    name: Seasonality name, like 'daily', 'weekly'.
936    uncertainty: Optional boolean to plot uncertainty intervals, which will
937        only be done if m.uncertainty_samples > 0.
938    figsize: Set the plot's size (in px).
939
940    Returns
941    -------
942    A Plotly Figure.
943    """
944    props = get_seasonality_plotly_props(m, name, uncertainty)
945    layout = go.Layout(
946        width=figsize[0],
947        height=figsize[1],
948        showlegend=False,
949        xaxis=props['xaxis'],
950        yaxis=props['yaxis']
951    )
952    fig = go.Figure(data=props['traces'], layout=layout)
953    return fig

Plot a custom seasonal component using Plotly. See plot_plotly() for Plotly setup instructions

Parameters
  • m (Prophet model.):

  • name (Seasonality name, like 'daily', 'weekly'.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • figsize (Set the plot's size (in px).):
Returns
  • A Plotly Figure.
def get_forecast_component_plotly_props( m: 'Prophet', fcst: pandas.DataFrame, name: str, uncertainty: bool = True, plot_cap: bool = False) -> '_PlotlyProps':
 956def get_forecast_component_plotly_props(
 957    m: Prophet,
 958    fcst: pd.DataFrame,
 959    name: str,
 960    uncertainty: bool = True,
 961    plot_cap: bool = False,
 962) -> _PlotlyProps:
 963    """Prepares a dictionary for plotting the selected forecast component with Plotly
 964
 965    Parameters
 966    ----------
 967    m: Prophet model.
 968    fcst: pd.DataFrame output of m.predict.
 969    name: Name of the component to plot.
 970    uncertainty: Optional boolean to plot uncertainty intervals, which will
 971        only be done if m.uncertainty_samples > 0.
 972    plot_cap: Optional boolean indicating if the capacity should be shown
 973        in the figure, if available.
 974
 975    Returns
 976    -------
 977    A dictionary with Plotly traces, xaxis and yaxis
 978    """
 979    prediction_color = '#0072B2'
 980    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
 981    cap_color = 'black'
 982    zeroline_color = '#AAA'
 983    line_width = 2
 984
 985    range_margin = (fcst['ds'].max() - fcst['ds'].min()) * 0.05
 986    range_x = [fcst['ds'].min() - range_margin, fcst['ds'].max() + range_margin]
 987
 988    text = None
 989    mode = 'lines'
 990    if name == 'holidays':
 991        
 992        # Combine holidays into one hover text
 993        holidays = m.construct_holiday_dataframe(fcst['ds'])
 994        holiday_features, _, _ = m.make_holiday_features(fcst['ds'], holidays)
 995        holiday_features.columns = holiday_features.columns.str.replace('_delim_', '', regex=False)
 996        holiday_features.columns = holiday_features.columns.str.replace('+0', '', regex=False)
 997        text = pd.Series(data='', index=holiday_features.index)
 998        for holiday_feature, idxs in holiday_features.items():
 999            # https://github.com/facebook/pyrefly/issues/2248
1000            # pyrefly:ignore[unsupported-operation]
1001            text[idxs.astype(bool) & (text != '')] += '<br>'  # Add newline if additional holiday
1002            text[idxs.astype(bool)] += holiday_feature  # pyrefly:ignore[unsupported-operation]
1003
1004    traces = []
1005    traces.append(go.Scatter(
1006        name=name,
1007        x=fcst['ds'],
1008        y=fcst[name],
1009        mode=mode,
1010        line=go.scatter.Line(color=prediction_color, width=line_width),
1011        text=text,
1012    ))
1013    if uncertainty and m.uncertainty_samples and (fcst[name + '_upper'] != fcst[name + '_lower']).any():
1014        if mode == 'markers':
1015            traces[0].update(
1016                error_y=dict(
1017                    type='data',
1018                    symmetric=False,
1019                    array=fcst[name + '_upper'],
1020                    arrayminus=fcst[name + '_lower'],
1021                    width=0,
1022                    color=error_color
1023                )
1024            )
1025        else:
1026            traces.append(go.Scatter(
1027                name=name + '_upper',
1028                x=fcst['ds'],
1029                y=fcst[name + '_upper'],
1030                mode=mode,
1031                line=go.scatter.Line(width=0, color=error_color)
1032            ))
1033            traces.append(go.Scatter(
1034                name=name + '_lower',
1035                x=fcst['ds'],
1036                y=fcst[name + '_lower'],
1037                mode=mode,
1038                line=go.scatter.Line(width=0, color=error_color),
1039                fillcolor=error_color,
1040                fill='tonexty'
1041            ))
1042    if 'cap' in fcst and plot_cap:
1043        traces.append(go.Scatter(
1044            name='Cap',
1045            x=fcst['ds'],
1046            y=fcst['cap'],
1047            mode='lines',
1048            line=go.scatter.Line(color=cap_color, dash='dash', width=line_width),
1049        ))
1050    if m.logistic_floor and 'floor' in fcst and plot_cap:
1051        traces.append(go.Scatter(
1052            name='Floor',
1053            x=fcst['ds'],
1054            y=fcst['floor'],
1055            mode='lines',
1056            line=go.scatter.Line(color=cap_color, dash='dash', width=line_width),
1057        ))
1058
1059    xaxis = go.layout.XAxis(
1060        type='date',
1061        range=range_x)
1062    yaxis = go.layout.YAxis(rangemode='normal' if name == 'trend' else 'tozero',
1063                            title=go.layout.yaxis.Title(text=name),
1064                            zerolinecolor=zeroline_color)
1065    assert m.component_modes
1066    if name in m.component_modes['multiplicative']:
1067        yaxis.update(tickformat='%', hoverformat='.2%')
1068    return {'traces': traces, 'xaxis': xaxis, 'yaxis': yaxis}

Prepares a dictionary for plotting the selected forecast component with Plotly

Parameters
  • m (Prophet model.):

  • fcst (pd.DataFrame output of m.predict.):

  • name (Name of the component to plot.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

  • plot_cap (Optional boolean indicating if the capacity should be shown): in the figure, if available.
Returns
  • A dictionary with Plotly traces, xaxis and yaxis
def get_seasonality_plotly_props(m: 'Prophet', name: str, uncertainty: bool = True) -> '_PlotlyProps':
1071def get_seasonality_plotly_props(
1072    m: Prophet,
1073    name: str,
1074    uncertainty: bool = True,
1075) -> _PlotlyProps:
1076    """Prepares a dictionary for plotting the selected seasonality with Plotly
1077
1078    Parameters
1079    ----------
1080    m: Prophet model.
1081    name: Name of the component to plot.
1082    uncertainty: Optional boolean to plot uncertainty intervals, which will
1083        only be done if m.uncertainty_samples > 0.
1084
1085    Returns
1086    -------
1087    A dictionary with Plotly traces, xaxis and yaxis
1088    """
1089    prediction_color = '#0072B2'
1090    error_color = 'rgba(0, 114, 178, 0.2)'  # '#0072B2' with 0.2 opacity
1091    line_width = 2
1092    zeroline_color = '#AAA'
1093
1094    # Compute seasonality from Jan 1 through a single period.
1095    start = pd.to_datetime('2017-01-01 0000')
1096    period = m.seasonalities[name]['period']
1097    end = start + pd.Timedelta(days=period)
1098    assert m.history is not None
1099    if (m.history['ds'].dt.hour == 0).all():  # Day Precision
1100        plot_points = np.floor(period).astype(int)
1101    elif (m.history['ds'].dt.minute == 0).all():  # Hour Precision
1102        plot_points = np.floor(period * 24).astype(int)
1103    else:  # Minute Precision
1104        plot_points = np.floor(period * 24 * 60).astype(int)
1105    days = pd.to_datetime(np.linspace(start.value, end.value, plot_points, endpoint=False))
1106    df_y = seasonality_plot_df(m, days)
1107    seas = m.predict_seasonal_components(df_y)
1108
1109    traces = []
1110    traces.append(go.Scatter(
1111        name=name,
1112        x=df_y['ds'],
1113        y=seas[name],
1114        mode='lines',
1115        line=go.scatter.Line(color=prediction_color, width=line_width)
1116    ))
1117    if uncertainty and m.uncertainty_samples and (seas[name + '_upper'] != seas[name + '_lower']).any():
1118        traces.append(go.Scatter(
1119            name=name + '_upper',
1120            x=df_y['ds'],
1121            y=seas[name + '_upper'],
1122            mode='lines',
1123            line=go.scatter.Line(width=0, color=error_color)
1124        ))
1125        traces.append(go.Scatter(
1126            name=name + '_lower',
1127            x=df_y['ds'],
1128            y=seas[name + '_lower'],
1129            mode='lines',
1130            line=go.scatter.Line(width=0, color=error_color),
1131            fillcolor=error_color,
1132            fill='tonexty'
1133        ))
1134
1135    # Set tick formats (examples are based on 2017-01-06 21:15)
1136    if period <= 2:
1137        tickformat = '%H:%M'  # "21:15"
1138    elif period < 7:
1139        tickformat = '%A %H:%M'  # "Friday 21:15"
1140    elif period < 14:
1141        tickformat = '%A'  # "Friday"
1142    else:
1143        tickformat = '%B %e'  # "January  6"
1144
1145    range_margin = (df_y['ds'].max() - df_y['ds'].min()) * 0.05
1146    xaxis = go.layout.XAxis(
1147        tickformat=tickformat,
1148        type='date',
1149        range=[df_y['ds'].min() - range_margin, df_y['ds'].max() + range_margin]
1150    )
1151
1152    yaxis = go.layout.YAxis(title=go.layout.yaxis.Title(text=name),
1153                            zerolinecolor=zeroline_color)
1154    if m.seasonalities[name]['mode'] == 'multiplicative':
1155        yaxis.update(tickformat='%', hoverformat='.2%')
1156
1157    return {'traces': traces, 'xaxis': xaxis, 'yaxis': yaxis}

Prepares a dictionary for plotting the selected seasonality with Plotly

Parameters
  • m (Prophet model.):

  • name (Name of the component to plot.):

  • uncertainty (Optional boolean to plot uncertainty intervals, which will): only be done if m.uncertainty_samples > 0.

Returns
  • A dictionary with Plotly traces, xaxis and yaxis