Skip to content

refactor: compute component plot data in private helpers - #2756

Merged
tcuongd merged 1 commit into
facebook:mainfrom
Thanatos9404:refactor-plot-component-data
Oct 21, 2026
Merged

tcuongd merged 1 commit into
facebook:mainfrom
Thanatos9404:refactor-plot-component-data

Conversation

@Thanatos9404

Copy link
Copy Markdown
Contributor

Closes #1988

As suggested in #1988, this moves the code that computes the data for the component plots out of the plotting functions and into two private helpers in prophet/plot.py, parametrized by the component name. Both the matplotlib and the plotly functions now take their data from them, so the data can be reused (for example by other plotting backends, or to get the numbers behind a plot as in #1920).

  • _forecast_component_plot_data(m, fcst, name, uncertainty=True, plot_cap=False) returns the data for trend, holidays and the extra_regressors_* components as a dataframe: ds and name, plus name_lower and name_upper when the uncertainty interval is shown, plus cap and floor when plot_cap is set and they are available. Used by plot_forecast_component and get_forecast_component_plotly_props.
  • _seasonality_plot_data(m, name, ds, uncertainty=True) evaluates a seasonality on the given dates (seasonality_plot_df followed by predict_seasonal_components) and returns ds and name, plus name_lower and name_upper when the interval is shown. Used by plot_weekly, plot_yearly, plot_seasonality and get_seasonality_plotly_props.

The rules for what goes into the data are the same as before: the interval needs uncertainty and m.uncertainty_samples, the cap needs 'cap' in fcst and plot_cap, and the floor needs m.logistic_floor and 'floor' in fcst and plot_cap. The plotting functions now draw whichever columns the helper returns, and plotly still leaves out an interval whose bounds are all equal.

Each plotting function still picks its own dates for a seasonality. The two backends use different grids today (7 days, 365 days or 200 points for matplotlib; one point per day, hour or minute of the history's precision for plotly), and those grids are left as they are so that no plot changes. There are no public API changes and no new dependencies.

Left for possible follow-ups: the holiday hover text of the plotly holidays plot, and the choice and order of the panels in plot_components and plot_components_plotly (the two backends differ there, so merging that logic would change one of them).

Tests

New prophet/tests/test_plot.py (20 tests; there were no plot tests before). It fits models with holidays, an extra regressor, a custom seasonality and a conditional seasonality, as additive/linear and as multiplicative/logistic with cap and floor, plus one with uncertainty_samples=0, and checks that:

  • _forecast_component_plot_data returns the forecast columns, with and without the interval, with and without cap and floor, and without error when the forecast has no _lower/_upper columns at all;
  • _seasonality_plot_data matches predict_seasonal_components(seasonality_plot_df(m, ds)) exactly (including the conditional seasonality, which is evaluated with its condition on);
  • plot_forecast_component, plot_weekly, plot_yearly, plot_seasonality and the two plotly props functions draw exactly the helper data, and draw an interval only when the helper returns one.

The same 20 tests also pass against the unchanged plot.py from main with only the two helpers appended, so the plots on main already draw exactly this data.

Before/after comparison: a script rendered every component plot from main and from this branch using the same saved models and forecasts. The models were: additive with MCMC (so the seasonal intervals are not empty) with US holidays, a regressor, a custom and a conditional seasonality; multiplicative logistic with cap and floor, custom holidays, regressors in both modes and a second yearly-period seasonality; uncertainty_samples=0; and 5 minute sub-daily data. It covered plot_components (all combinations of uncertainty, plot_cap, weekly_start and yearly_start), plot_forecast_component, plot_weekly, plot_yearly, plot_seasonality, plot_components_plotly, plot_forecast_component_plotly and plot_seasonality_plotly. For the 146 matplotlib figures it recorded the line data, the fill_between vertices, ticks, tick labels, axis labels, limits and a SHA-256 of the PNG rendered with Agg; for the 86 plotly figures, fig.to_json(). All 232 outputs are byte-identical between main and this branch. As a check of the comparison itself, scaling the helper output by 1 + 1e-15 and dropping the floor made 160 of the 232 outputs differ.

Tested with:

  • Python 3.12.5, numpy 2.5.4, pandas 3.0.6, matplotlib 3.11.2, plotly 7.1.0, cmdstanpy 1.3.0, pyrefly 0.50.0 on Windows 11, using the compiled Stan model from the prophet==1.5.0 wheel with the repository sources first on PYTHONPATH.
  • Python 3.10.20, numpy 1.26.4, pandas 1.5.3, matplotlib 3.6.3, plotly 5.24.1 for test_plot.py and the before/after comparison.

Results:

  • python -m pytest prophet/tests/ (Python 3.12): main 118 passed, 2 skipped; this branch 138 passed, 2 skipped (the same 118 plus the 20 new tests).
  • python -m pytest prophet/tests/test_plot.py: 20 passed on both Python 3.12 and Python 3.10.
  • pyrefly check: 0 errors (18 suppressed) on both main and this branch.
  • Before/after comparison: 232 of 232 outputs identical on Python 3.12, and 232 of 232 on Python 3.10.

Add _forecast_component_plot_data and _seasonality_plot_data to
prophet/plot.py. They return the data that the component plots draw
(the component values, the uncertainty interval and, for forecast
components, the cap and floor) as a dataframe. The matplotlib and
plotly plotting functions now take their data from these helpers
instead of computing it inline, so the same data can be reused by
other plotting code.

The plots are unchanged. New tests in tests/test_plot.py check the
helpers against the forecast and predict_seasonal_components, and
check that both backends draw exactly the helper data.
@meta-cla meta-cla Bot added the CLA Signed label Oct 13, 2026
@tcuongd

tcuongd commented Oct 21, 2026

Copy link
Copy Markdown
Collaborator

It'd be good to show the plots before / after (doesn't have to be all of them) to validate visually.

@Thanatos9404

Copy link
Copy Markdown
Contributor Author

Sure, here are four of them. In each image the left half is main (0cf18fc) and the right half is this branch, drawn from the same fitted model and forecast. The two halves are also pixel identical.

Additive model with US holidays and an extra regressor, plot_components:

plot_components, additive model with holidays and a regressor

Logistic growth with cap and floor and multiplicative seasonality, plot_components(..., plot_cap=True):

plot_components, logistic model with cap and floor

MCMC fit with a custom monthly seasonality, so the seasonality intervals are drawn:

plot_components, MCMC model with seasonality intervals

The first model with plot_components_plotly:

plot_components_plotly, additive model

@tcuongd
tcuongd merged commit ad059eb into facebook:main Oct 21, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Split up the code that calculates data for component plots

2 participants