learn-timeseries-forecasting-with-phoebe / Builder session 5 of 10
Learn Timeseries Forecasting with Phoebe · Builder track · Session 5 of 10

Prophet: decomposable, analyst-friendly forecasting

ARIMA asks you to think in differences and lags. Prophet asks a simpler question: what is the trend, what repeats, and what are the known special days? It models a series as an additive stack - trend + seasonality + holidays + error - fits automatic changepoints and Fourier seasonality, and hands back an uncertainty band for free. Tonight we fit it on the Lumen sessions series, read its parts, teach it a Lumen sale day, and hold it honest against the b1 baseline.

🟡 Builder track Practitioners: analysts · data scientists · engineers Python 3.10+ · a notebook ~45 min
0-3 · Recap 3-18 · The additive model 18-42 · Build-along: fit + components + holidays 42-45 · Q&A
Part 0

Where Prophet sits on the ladder

We have climbed from baselines (b2) through exponential smoothing (b3) to ARIMA/SARIMA (b4). Prophet, from Facebook's Taylor & Letham, takes a different path: instead of modelling the autocorrelation structure, it fits a decomposable curve - a trend, one or more seasonal cycles, and a holiday effect - and adds them up. It is fast to a good result, forgiving of missing data and outliers, and every part is something an analyst can read and explain. The trade: it is a curve-fitter, not automatically the most accurate model. So we do the same thing we do to every model in this course - we backtest it against the baseline before we trust it.

Live - presented in session Self-study - read after class ★ Run it now (Python) Sources covered (cited)
★ What you walk out with today The Lumen series reshaped into Prophet's ds/y frame, a fitted model with a forecast for the next H days plus its yhat_lower/yhat_upper interval, the decomposed trend and weekly components read straight off plot_components, a holidays table that teaches the model about a Lumen sale day, and a backtested MAPE you can set beside the b1 seasonal-naive baseline.
Part 1 · covers Taylor & Letham 2018

A forecast built from readable parts 6 min live

Prophet writes the series as y(t) = g(t) + s(t) + h(t) + ε: a trend g, seasonality s, holidays h, and an error term. Each part is estimated and then stacked back together. Because the pieces are additive, you can plot each one on its own and say exactly why the forecast bends where it does - that interpretability is the whole selling point.

y(t) = trend + seasonality + holidays + error Trend g(t) bends at changepoints Season s(t) weekly Fourier terms Holidays h(t) spikes on known days = Forecast yhat + interval Each part is estimated, then summed - read the trend, the weekly rhythm, and holidays on their own.
🔍 Click to zoom - Prophet = trend + seasonality + holidays, stacked into a forecast with a band
LiveThe four parts, one at a time3 min▶
  • Trend g(t) - a piecewise-linear (or logistic) growth curve. Prophet places changepoints automatically and lets the slope shift at each, so a series that speeds up or flattens is captured without you naming the dates.
  • Seasonality s(t) - repeating cycles built from Fourier terms (sines and cosines). Weekly and yearly are the defaults; each is a smooth periodic curve, not a lookup table.
  • Holidays h(t) - a table of known special days (sales, launches, public holidays) that get their own effect, with optional windows around them.
  • Error ε - the irregular part Prophet does not try to predict, which drives the width of the uncertainty interval.
Interpretability is the feature Because the model is additive, plot_components shows you the trend, the weekly shape, and the holiday bumps as separate panels. When a stakeholder asks "why does the forecast dip on Saturdays?", you point at the weekly panel. That explain-ability is why analyst teams reach for Prophet first.
Self-studyStrengths, cautions, and when to reach for it2 min read▶

Prophet earns its popularity, but it is a tool with a shape:

  • Strengths - fast to a good result, handles missing days and outliers gracefully, needs little tuning, and produces interpretable components plus an interval out of the box.
  • Cautions - it is fundamentally a curve-fitter. It does not model autocorrelation the way ARIMA does, and on some series a well-tuned SARIMA or a gradient-boosting model (b6) will beat it. It can also over-fit wiggly trends if you loosen the changepoint prior too far.
  • Reach for it when you have strong multiple seasonalities, known holiday effects, and want a defensible, explainable forecast quickly - then backtest it against the baseline like everything else.
Real world

Prophet was built for exactly Lumen's problem. Taylor & Letham designed it at Facebook for business time series with human-scale seasonality and holiday spikes, forecast by analysts who are not time-series specialists. Lumen's daily sessions - a weekly rhythm plus sale-day spikes - is squarely in that sweet spot.

Part 2 · covers the trend model

Changepoints: where the trend is allowed to bend 5 min live

A straight-line trend cannot describe a series that accelerates then plateaus. Prophet solves this by putting a set of candidate changepoints along the history and letting the slope change at each. It shrinks most of those changes toward zero, so only the changepoints that really matter bend the line. The result is a flexible-but-not-jumpy trend you can point at.

The trend bends only where the data insists changepoint 1 changepoint 2 observed sessions fitted trend slope bends only at the changepoints that matter
🔍 Click to zoom - a piecewise-linear trend bends at detected changepoints
LiveThe two knobs you will actually touch3 min▶

Prophet is deliberately low-config, but two settings control most of its behaviour:

  • changepoint_prior_scale - how freely the trend may bend. Higher = more flexible (risk of over-fitting); lower = stiffer, smoother trend. This is your first dial when the trend looks too wiggly or too rigid.
  • seasonality_mode - "additive" when the weekly swing is a constant size, "multiplicative" when the swing grows with the level (a bigger site has bigger Saturday dips in absolute terms). Same additive-vs-multiplicative call you met in b3.
  • interval_width - the coverage of the uncertainty band (default 0.8). Set it to the interval your decision needs, and remember it widens with the horizon.
Honest by default Prophet never hands you a bare point forecast - predict always returns yhat with yhat_lower and yhat_upper. Report the band, not just the line. A forecast without an interval is a guess wearing false confidence.
Demo 1 of 2

Reshape, fit, and forecast H days ★ 12 min · everyone builds

Prophet has one hard requirement: a dataframe with a ds (datestamp) column and a y (value) column. Reshape the Lumen series into that shape, fit, extend the calendar by H days, and read the forecast plus its interval.

Rebuild the Lumen daily series from b1 and keep the training slice (everything but the last H days).

Reset the index into a two-column frame and rename the columns to ds and y.

Fit a Prophet model with weekly seasonality on, then make_future_dataframe(periods=H) to extend the calendar.

predict, and read yhat, yhat_lower, yhat_upper for the H forecast days.

★ Run it now (Python) - reshape to ds/y, fit, predictimport pandas as pd, numpy as np from prophet import Prophet # rebuild the Lumen daily sessions series (from b1): trend + weekly season + noise idx = pd.date_range("2026-01-01", periods=112, freq="D") season = np.array([40,55,60,50,30,-120,-140]) # weekday shape i = np.arange(112) y = 1000 + 2.5*i + season[i % 7] + 14*np.sin(i*1.7) + 9*np.cos(i*0.6) s = pd.Series(np.round(y), index=idx, name="sessions") H = 14 # forecast horizon train = s.iloc[:-H] # Prophet wants exactly two columns: ds (datestamp) and y (value) df = train.reset_index() df.columns = ["ds", "y"] m = Prophet(weekly_seasonality=True, yearly_seasonality=False, daily_seasonality=False, interval_width=0.9) m.fit(df) future = m.make_future_dataframe(periods=H) # extend calendar by H days forecast = m.predict(future) print(forecast[["ds", "yhat", "yhat_lower", "yhat_upper"]].tail(H))
Read the band, not just yhat Those last three columns are the whole point. yhat is the point forecast; yhat_lower/yhat_upper are the 90% interval we asked for with interval_width=0.9. When you plot or hand this off, carry all three - the interval is what makes the forecast honest.
Demo 2 of 2

Read the components, add a holiday, and score it ★ 10 min · build your own

First look inside the model with plot_components - the trend and the weekly shape drawn separately. Then teach Prophet about a Lumen sale day with a holidays table, refit, and score its MAPE on the holdout against the b1 seasonal-naive baseline.

Call m.plot_components(forecast) to see the trend panel and the weekly-seasonality panel.

Build a small holidays dataframe for a Lumen sale day, with a one-day spill-over window.

Refit with holidays=, forecast H days, and compute MAPE on the test slice.

Compare that MAPE to the seasonal-naive baseline from b1 - did the complexity earn its place?

★ Run it now (Python) - components + holidays + MAPE vs baselineimport matplotlib.pyplot as plt # 1) look inside the fitted model: trend + weekly seasonality as separate panels m.plot_components(forecast) plt.show() # 2) teach Prophet about Lumen sale days (a known, repeatable event) holidays = pd.DataFrame({ "holiday": "lumen_sale", "ds": pd.to_datetime(["2026-02-14", "2026-03-28"]), "lower_window": 0, "upper_window": 1, # one spill-over day after }) mh = Prophet(weekly_seasonality=True, yearly_seasonality=False, daily_seasonality=False, holidays=holidays, interval_width=0.9) mh.fit(df) fc = mh.predict(mh.make_future_dataframe(periods=H)).tail(H) # 3) score on the untouched holdout, same metric as the baseline test = s.iloc[-H:] mape = (np.abs(test.values - fc["yhat"].values) / test.values).mean() * 100 print(f"Prophet + holidays MAPE: {mape:.1f}%") print("compare to the b1 seasonal-naive baseline - complexity must beat it to ship")
Real world

The holidays table is where domain knowledge enters. You know Lumen's sale calendar; the model does not. Giving it those dates - with a window for the day-after spill - lets it separate the sale spike from the ordinary weekly rhythm, so the underlying trend stays clean. But the verdict is still the backtest: if Prophet cannot beat seasonal-naive on the holdout, the seasonal-naive line ships and Prophet goes back for tuning. That is the M-competition humility, applied to every model in this course.

Homework

Try it yourself - this week ◐ 30-45 min total

Source material

Sources covered (cited)

Taught from the Prophet paper and the forecasting canon. This session covers:

✓Taylor & Letham (2018), "Forecasting at Scale" (Prophet), The American Statistician 72(1):37-45, DOI 10.1080/00031305.2017.1380080 (PeerJ Preprints 5:e3190, 2017)Parts 1-2 + both demos · the additive model, changepoints, Fourier seasonality, holidays, intervals
◐M-competitions (Makridakis, M4/M5)Demo 2 · the "beat the baseline or do not ship" discipline
Check yourself

Three questions before you go 🎯 ◐ 90 seconds

1 · How does Prophet model a series?

Prophet writes y(t) = g(t) + s(t) + h(t) + error. Each part is estimated and summed, which is why the components are individually readable.

2 · What are changepoints in Prophet?

Prophet puts candidate changepoints along the history and lets the trend slope shift at each, shrinking most toward zero so only the ones that matter bend the line.

3 · Prophet gives a fast, interpretable forecast. Do you ship it?

Prophet is a curve-fitter, not automatically the most accurate model. The M-competition rule holds: complexity must beat the baseline on a backtest, or the baseline ships.

Builder session 5 cheat sheet · pin this

Prophet modely(t) = trend + seasonality + holidays + error. Additive, decomposable, interpretable.
ds / y frameProphet needs two columns: ds (datestamp) and y (value). Reshape the series first.
Changepointsautomatic points where the trend slope may bend. Tune with changepoint_prior_scale.
Fourier seasonalityweekly/yearly cycles as smooth sine/cosine terms, not lookup tables.
Holidays tableknown special days (sales, launches) with optional windows. Domain knowledge enters here.
plot_componentsdraws trend + weekly + holiday panels separately - the "why" behind the forecast.
Always an intervalpredict returns yhat + yhat_lower + yhat_upper. Report the band; it widens with the horizon.
Backtest itfast and readable is not the same as accurate. Beat the b1 baseline or it does not ship.
Next sessionb6 - ML forecasting: turn the series into a supervised table and let gradient boosting learn it.