Prophet forecasts time series with trend, seasonality, and holiday effects. pip install prophet. from prophet import Prophet. Data format: df = pd.DataFrame({"ds": dates, "y": values}) — ds must be datetime. Fit: model = Prophet(), model.fit(df). Forecast: future = model.make_future_dataframe(periods=365), forecast = model.predict(future) — returns yhat yhat_lower yhat_upper. Plot: model.plot(forecast), model.plot_components(forecast). Seasonality mode: Prophet(seasonality_mode="multiplicative") for percentage trends. Holidays: model.add_country_holidays(country_name="US"). Custom seasonal: model.add_seasonality(name="monthly", period=30.5, fourier_order=5). External regressors: model.add_regressor("temperature") — column must be in both train and future df. Tuning: key params are changepoint_prior_scale (0.001-0.5, default 0.05 — higher = more flexible trend), seasonality_prior_scale (0.01-10, default 10), holidays_prior_scale. Changepoints: Prophet(changepoint_range=0.8, n_changepoints=25). Uncertainty: Prophet(uncertainty_samples=1000). Cross-validation: from prophet.diagnostics import cross_validation, performance_metrics, df_cv = cross_validation(model, initial="730 days", period="180 days", horizon="365 days"), df_p = performance_metrics(df_cv). Logistic growth: Prophet(growth="logistic") with cap and floor columns. Claude Code generates Prophet forecasting pipelines, hyperparameter tuners, cross-validation scripts, and multi-series batch forecasters.
CLAUDE.md for Prophet
## Prophet Stack
- Version: prophet >= 1.1
- Data: pd.DataFrame({"ds": datetime_series, "y": float_series})
- Fit: Prophet(**params).fit(df)
- Forecast: model.make_future_dataframe(periods=N) → model.predict(future)
- Key params: changepoint_prior_scale (0.05) | seasonality_prior_scale (10)
- Holidays: model.add_country_holidays("US") | custom holiday DataFrame
- Custom seasonality: add_seasonality(name, period, fourier_order)
- Regressors: add_regressor("col") — must exist in both train and future df
- Cross-val: cross_validation(model, initial, period, horizon) → performance_metrics
Prophet Forecasting Pipeline
# ml/prophet_pipeline.py — time series forecasting with Meta Prophet
from __future__ import annotations
import warnings
import itertools
import numpy as np
import pandas as pd
from pathlib import Path
warnings.filterwarnings("ignore")
from prophet import Prophet
from prophet.diagnostics import cross_validation, performance_metrics
from prophet.plot import add_changepoints_to_plot
# ── 1. Data helpers ───────────────────────────────────────────────────────────
def make_prophet_df(
dates: pd.DatetimeIndex | list,
values: np.ndarray | list,
cap: float = None,
floor: float = None,
) -> pd.DataFrame:
"""
Create a Prophet-compatible DataFrame.
cap/floor are required for logistic growth models.
"""
df = pd.DataFrame({"ds": pd.to_datetime(dates), "y": values})
if cap is not None:
df["cap"] = cap
if floor is not None:
df["floor"] = floor
return df
def resample_series(
df: pd.DataFrame,
freq: str = "D", # "H" hourly, "D" daily, "W" weekly, "M" monthly
agg: str = "sum", # "sum" | "mean" | "last"
) -> pd.DataFrame:
"""Resample time series to a different frequency."""
df = df.set_index("ds").sort_index()
resampled = getattr(df.resample(freq)["y"], agg)()
return resampled.reset_index().rename(columns={"index": "ds"})
# ── 2. Model building ─────────────────────────────────────────────────────────
def build_model(
growth: str = "linear", # "linear" | "logistic" | "flat"
seasonality_mode: str = "additive", # "additive" | "multiplicative"
changepoint_prior_scale: float = 0.05, # Higher = more flexible trend
seasonality_prior_scale: float = 10.0, # Higher = stronger seasonality
holidays_prior_scale: float = 10.0,
changepoint_range: float = 0.8,
n_changepoints: int = 25,
interval_width: float = 0.80, # Uncertainty interval (0.8 = 80% CI)
daily_seasonality: bool = "auto",
weekly_seasonality: bool = "auto",
yearly_seasonality: bool = "auto",
uncertainty_samples: int = 1000,
country_holidays: str = None,
) -> Prophet:
"""
Build Prophet model with configurable parameters.
changepoint_prior_scale tuning:
- 0.001-0.01: rigid trend (underfitting risk)
- 0.05: default, good starting point
- 0.1-0.5: flexible trend (overfitting risk)
seasonality_mode:
- additive: season amplitude is constant (most time series)
- multiplicative: season scales with trend level (revenue, growth metrics)
"""
model = Prophet(
growth=growth,
seasonality_mode=seasonality_mode,
changepoint_prior_scale=changepoint_prior_scale,
seasonality_prior_scale=seasonality_prior_scale,
holidays_prior_scale=holidays_prior_scale,
changepoint_range=changepoint_range,
n_changepoints=n_changepoints,
interval_width=interval_width,
daily_seasonality=daily_seasonality,
weekly_seasonality=weekly_seasonality,
yearly_seasonality=yearly_seasonality,
uncertainty_samples=uncertainty_samples,
)
if country_holidays:
model.add_country_holidays(country_name=country_holidays)
return model
# ── 3. Custom seasonalities and regressors ───────────────────────────────────
def add_custom_seasonalities(
model: Prophet,
seasonalities: list[dict],
) -> Prophet:
"""
Add custom Fourier-series seasonalities.
seasonalities = [
{"name": "quarterly", "period": 91.25, "fourier_order": 7},
{"name": "monthly", "period": 30.5, "fourier_order": 5},
]
"""
for s in seasonalities:
model.add_seasonality(**s)
return model
def add_regressors(
model: Prophet,
regressors: list[dict],
) -> Prophet:
"""
Add external regressors (must be available for forecast horizon).
regressors = [
{"name": "temperature", "prior_scale": 0.5},
{"name": "is_promo", "prior_scale": 10},
]
"""
for reg in regressors:
model.add_regressor(**reg)
return model
# ── 4. Training and forecasting ───────────────────────────────────────────────
def fit_and_forecast(
model: Prophet,
df: pd.DataFrame,
periods: int,
freq: str = "D",
include_history: bool = True,
regressor_future: pd.DataFrame = None,
) -> tuple[Prophet, pd.DataFrame]:
"""
Fit model and generate forecast.
Returns fitted model and forecast DataFrame.
forecast columns:
- ds, yhat: point forecast
- yhat_lower/upper: uncertainty interval
- trend, trend_lower/upper
- seasonal components (weekly, yearly, etc.)
"""
model.fit(df)
future = model.make_future_dataframe(
periods=periods, freq=freq, include_history=include_history
)
# Add regressor values for future dates if provided
if regressor_future is not None:
future = future.merge(regressor_future, on="ds", how="left")
forecast = model.predict(future)
return model, forecast
def extract_forecast(
forecast: pd.DataFrame,
last_n_rows: int = None,
) -> pd.DataFrame:
"""Extract forecast columns from full prediction DataFrame."""
cols = ["ds", "yhat", "yhat_lower", "yhat_upper", "trend"]
result = forecast[cols]
if last_n_rows:
result = result.tail(last_n_rows)
return result
# ── 5. Backtesting / cross-validation ────────────────────────────────────────
def backtest(
model: Prophet,
df: pd.DataFrame,
initial: str = "365 days", # Training window for first cutoff
period: str = "90 days", # Spacing between cutoffs
horizon: str = "30 days", # Forecast horizon to evaluate
parallel: str = "processes", # "processes" | "threads" | None
) -> dict[str, float]:
"""
Run time-series cross-validation and return performance metrics.
initial: must be >= 2 * horizon
period: smaller = more cutoffs (slower, more thorough)
horizon: evaluation window (what you care about forecasting)
"""
model_copy = model.__class__(**{
k: v for k, v in model.__dict__.items()
if not k.startswith("_") and k not in {"history", "params"}
})
model_copy.fit(df)
df_cv = cross_validation(
model_copy, initial=initial, period=period,
horizon=horizon, parallel=parallel
)
df_metrics = performance_metrics(df_cv)
# Summary metrics (mean across horizons)
summary = {
"mae": round(float(df_metrics["mae"].mean()), 4),
"mape": round(float(df_metrics["mape"].mean()), 4),
"rmse": round(float(df_metrics["rmse"].mean()), 4),
"mdape": round(float(df_metrics["mdape"].mean()), 4),
}
return summary
# ── 6. Hyperparameter tuning ──────────────────────────────────────────────────
def tune_prophet(
df: pd.DataFrame,
initial: str = "365 days",
period: str = "90 days",
horizon: str = "30 days",
param_grid: dict = None,
) -> dict:
"""
Grid search over Prophet hyperparameters using CV.
Returns best parameters by MAPE.
"""
if param_grid is None:
param_grid = {
"changepoint_prior_scale": [0.01, 0.05, 0.1, 0.5],
"seasonality_prior_scale": [0.1, 1.0, 10.0],
"seasonality_mode": ["additive", "multiplicative"],
}
all_params = [
dict(zip(param_grid.keys(), v))
for v in itertools.product(*param_grid.values())
]
best_mape = float("inf")
best_params = {}
for params in all_params:
model = Prophet(**params, uncertainty_samples=0)
model.fit(df)
df_cv = cross_validation(
model, initial=initial, period=period,
horizon=horizon, parallel=None
)
df_m = performance_metrics(df_cv, rolling_window=1)
mape = float(df_m["mape"].values[0])
params_str = " ".join(f"{k}={v}" for k, v in params.items())
print(f" {params_str} MAPE={mape:.4f}")
if mape < best_mape:
best_mape = mape
best_params = params
print(f"\nBest MAPE={best_mape:.4f}: {best_params}")
return best_params
# ── 7. Multi-series batch forecasting ────────────────────────────────────────
def forecast_multiple_series(
series_dict: dict[str, pd.DataFrame], # {"series_name": prophet_df}
periods: int = 30,
freq: str = "D",
model_params: dict = None,
) -> dict[str, pd.DataFrame]:
"""
Fit and forecast multiple independent time series.
Returns {series_name: forecast_df}.
"""
model_params = model_params or {}
results = {}
for name, df in series_dict.items():
m = Prophet(**model_params)
_, forecast = fit_and_forecast(m, df, periods=periods, freq=freq)
results[name] = extract_forecast(forecast, last_n_rows=periods)
print(f" Forecasted: {name} ({periods} periods)")
return results
# ── Demo ──────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("Prophet Forecasting Demo")
print("="*50)
# Generate synthetic daily sales data with trend + seasonality
dates = pd.date_range("2021-01-01", "2024-01-01", freq="D")
n = len(dates)
t = np.arange(n)
trend = 100 + 0.05 * t
weekly = 10 * np.sin(2 * np.pi * t / 7)
yearly = 50 * np.sin(2 * np.pi * t / 365)
noise = np.random.normal(0, 5, n)
y = trend + weekly + yearly + noise
df = make_prophet_df(dates, y)
print(f"Dataset: {len(df)} daily observations")
# Fit and forecast
model = build_model(
changepoint_prior_scale=0.05,
seasonality_mode="additive",
country_holidays="US",
)
model, forecast = fit_and_forecast(model, df, periods=90, freq="D")
next_30 = extract_forecast(forecast, last_n_rows=30)
print(f"\nForecasted 30 days:")
print(next_30.head(5).to_string(index=False))
future_yhat = next_30["yhat"].values
print(f"\n30-day range: {future_yhat.min():.1f} – {future_yhat.max():.1f}")
print(f"90-day forecast: {forecast['yhat'].tail(90).mean():.1f} avg/day")
For the statsmodels ARIMA/SARIMA alternative when needing strict stationarity tests, ACF/PACF diagnostics, and AIC-based model selection for classical time series econometrics — ARIMA provides rigorous statistical inference while Prophet is more robust to missing data, outliers, non-stationarity, and multiple seasonal periods without requiring stationarity transformations, making it practical for business analysts and data scientists who need reliable forecasts without deep time series expertise. For the NeuralProphet/N-BEATS alternative when needing neural network components to capture complex non-linear patterns that Prophet’s additive decomposition misses — NeuralProphet extends Prophet with AR-Net autoregression while Prophet’s decomposable model (trend + seasonality + holidays) is more interpretable via plot_components, easier to incorporate domain knowledge through add_regressor, and more robust on short series (< 2 years) where neural methods overfit. The Claude Skills 360 bundle includes Prophet skill sets covering DataFrame preparation, model configuration, custom seasonalities, external regressors, holiday effects, fit and forecast, cross-validation with performance metrics, hyperparameter grid search, and multi-series batch forecasting. Start with the free tier to try time series forecasting code generation.