10.6 Prophet: Facebook's Additive Regression Model
Right, let’s talk about Prophet. You’ve probably hit the wall with ARIMA models, fussing over p, d, and q parameters like you’re trying to crack a safe. Facebook’s Core Data Science team felt your pain and built Prophet, an additive regression model that handles a lot of the time series nastiness for you. It’s not magic, but it’s the closest thing we’ve got for a lot of common forecasting problems. The core idea is brilliant in its simplicity: decompose your time series into three main components—trend, seasonality, and holidays. Then, you just add them all back together. Hence, “additive model.” Simple, right? Let’s get into it.
The Guts of the Model
Prophet fits this equation:
y(t) = g(t) + s(t) + h(t) + ε_t
Where g(t) is the trend function (modeling non-periodic changes), s(t) represents seasonal changes (weekly, yearly, etc.), h(t) captures holiday effects, and ε_t is the error term (which Prophet assumes is normally distributed). The beauty is that you don’t need to know the math inside out to use it, but understanding this helps you know why you’re tuning certain parameters later.
The trend g(t) is often modeled with either a saturating growth model (logistic growth) or a piecewise linear model. For most business forecasting where things don’t asymptote to a carrying capacity, the linear model is your go-to. It fits a series of straight lines connected at “changepoints,” which are points in time where the trend rate is allowed to change. Prophet automatically places changepoints for you, but you can—and often should—tweak how sensitive it is to them.
Your First Forecast: A Code Example
Let’s say you have a daily series of data. Here’s the absolute minimum you need to get a forecast. First, get Prophet installed (pip install prophet). Now, run this.
import pandas as pd
from prophet import Prophet
# Prophet is opinionated. It demands columns be named 'ds' and 'y'.
df = pd.read_csv('your_data.csv') # Assume it has 'date' and 'sales'
df['ds'] = pd.to_datetime(df['date'])
df['y'] = df['sales']
# Fit the model. This is where the magic happens.
model = Prophet() # The vanilla, out-of-the-box model
model.fit(df)
# Create a dataframe to hold future dates. The 'periods' is how many steps forward.
future = model.make_future_dataframe(periods=365) # Forecast for a year
# Predict! The forecast DataFrame gets all the juicy components.
forecast = model.predict(future)
# Plot the whole shebang. This is incredibly useful.
fig1 = model.plot(forecast)
This will spit out a perfectly reasonable, if perhaps a bit vanilla, forecast. The forecast dataframe contains columns for the predicted value (yhat), as well as its components (trend, weekly, yearly) and uncertainty intervals (yhat_lower, yhat_upper).
Taming the Trend Changepoints
Here’s where most people get into trouble. Prophet automatically places 25 changepoints evenly throughout the first 80% of your historical data. This is a sane default, but it’s often too flexible, leading to the model overfitting to weird little wobbles in your history and producing a wildly oscillating forecast that looks like it’s having a panic attack.
You need to control two things:
changepoint_prior_scale: This is the single most important hyperparameter. It controls how much the trend is allowed to change at those changepoints. A higher value (e.g., 0.05) means a more flexible trend (more wiggly). A lower value (e.g., 0.001) makes the trend more rigid. If your forecast is too wiggly, turn this down.changepoint_range: The proportion of history in which to place changepoints (default 0.8). If you know your recent history is more relevant, you can extend this to 0.9 or even 1.0.
# A more robust model setup
model = Prophet(
changepoint_prior_scale=0.01, # More rigid trend
changepoint_range=0.9, # Allow changepoints in 90% of history
yearly_seasonality=True, # These are usually on by default
weekly_seasonality=True,
daily_seasonality=False # Turn off what you don't need!
)
Wrestling with Seasonality
Prophet uses Fourier terms to model seasonality, which is a fancy way of saying it uses a bunch of sine and cosine waves to approximate complex seasonal patterns. The number of Fourier terms per seasonality is controlled by the seasonality_mode (additive or multiplicative) and the fourier_order. A higher order captures more detailed seasonality but also risks overfitting.
The default additive seasonality assumes the seasonal effect is constant regardless of the trend’s height. If your time series shows that seasonal swings get bigger as the trend grows (classic in business growth), you need multiplicative seasonality.
# Let's get more specific with our seasonality
model = Prophet(
yearly_seasonality=8, # Increase the Fourier order for more complex yearly patterns
seasonality_mode='multiplicative' # For when seasonality scales with trend
)
# You can also add custom seasonality for, say, a quarterly effect
model.add_seasonality(name='quarterly', period=91.25, fourier_order=4)
The Holiday Problem (or “The Trap”)
Holidays are both Prophet’s killer feature and its easiest way to shoot yourself in the foot. You provide a list of holiday dates, and Prophet models an effect for them. The critical, often-missed detail: it assumes this effect is additive and consistent for a window around the holiday. This is fantastic for Black Friday. It’s disastrous for a holiday like Christmas, where the effect isn’t just on the day but involves a complex week-long build-up and drop-off.
If you just blindly add a standard US holidays list, your model will try to find a consistent “bump” on December 25th, which will likely mess up the entire surrounding week. You must be surgical. Define the specific days you want to model and consider the lower-level add_regressor function for complex events.
# A better way to handle a complex holiday like Christmas
christmas_df = pd.DataFrame({
'holiday': 'christmas',
'ds': pd.to_datetime(['2020-12-24', '2020-12-25', '2020-12-26',
'2021-12-24', '2021-12-25', '2021-12-26']), # ... add all years
'lower_window': -1, # Effect starts one day before ('ds')
'upper_window': 1, # Effect ends one day after ('ds')
})
model = Prophet(holidays=christmas_df)
Final Reality Check
Prophet is a powerful tool, not a crystal ball. Its greatest strength—making reasonable defaults—is also its greatest weakness. It will happily produce a beautiful, confident, and completely wrong forecast if you feed it garbage or misapply it. Always:
- Plot your components (
model.plot_components(forecast)). Does the trend make sense? Does the weekly seasonality look logical (e.g., higher sales on weekends)? - Hold out some data and measure error. Don’t just look at the pretty graph.
- Question the uncertainty intervals. They are based on historical volatility. If your future is fundamentally different from your past (a new competitor, a pandemic), those intervals are a fantasy.
It’s a model that rewards a thoughtful, iterative approach. Tune it, question it, and for heaven’s sake, don’t just accept the first forecast it gives you.