What's different
Effect Prophet fits the same model as Python Prophet 1.4.0 (see How close are the results?). This page lists where it differs on purpose, and the smaller behavior differences, including limits Python doesn't have.
Different on purpose
Plain data and typed errors instead of a mutable
Prophetobject and pandas dataframes. Rows are{ timestamp, value }instead ofdsandy.Strict UTC timestamps; no guessing at formats or time zones
Missing values are rejected rather than silently dropped
makeFutureTimestampsaccepts only the pandas frequenciesh,D,W,W-SUNtoW-SAT,MSandME, never infers one (freq=None), and generates at most 100,000 future dates per callNo built-in plotting
Saved models use their own format, not Python's JSON
Runs in browsers as well as Node.js, with no Python process or server. See Browsers and bundlers.
Country calendars are offline Prophet 1.4.0 /
holidays0.104 snapshots with an outer 1995–2060 horizon. Known missing coverage is rejected unless explicitly accepted withcoverage: "allow-known-gaps"; neither policy guarantees completeness. Only country-level defaults are supported, not subdivisions/languages/categories. Saved models embed their selected snapshot and never silently adopt installed updates. See Country holiday calendars.Cross-validation and model search run one fold after another, with no counterpart to
cross_validation'sparallelargument. The benchmarks time Python's parallel modes next to Effect Prophet's sequential runs. The two return different outputs, so the times show each library's cost rather than a speed ranking.Cross-validation rows have the cutoff, timestamp, horizon, actual value and forecast, plus the range when you ask for intervals. There's no counterpart to
cross_validation'sextra_output_columns, which copies otherpredictcolumns, such astrend, into each row.Custom metrics are definitions you pass to each call, not entries in a global registry, and can't replace a built-in metric. Each scores one forecast at a time and combines a group with a mean, median, quantile or ratio of means, using Prophet's windows. Python's
register_performance_metrictakes any function of the whole result. Python has no quantile helper; the quantile follows numpy'snp.quantileover the windowsmdapeuses. See Your own metrics.scaledErrorMetricadds mean absolute scaled error (MASE), which Python doesn't have. Each round's scale comes from a naive rule's error on that round's training rows. See Scaled errors. The benchmarks check it against MASE registered in Python withregister_performance_metric.Saved models keep the models that forecast their regressors, so a loaded model forecasts exactly as before. Python's
model_to_jsondrops them, and a loaded model then asks for the regressor values.A regressor model's
predictor: {}uses the default settings, likepredictor: true. In Python,regressor_predictor={}counts as false and fits no regressor model.predictnever simulates, so point forecasts never pay for it. Ranges come frompredictWithIntervals, orpredictUncertaintyfor ranges alone. They need a seed, so results are repeatable.Full Bayesian fitting needs a
seed; Python picks a random one when you don't give one. See Full Bayesian fitting.
Small behavior differences
Bayesian chains. The sampler is a port of Stan's NUTS, but its chains can't match CmdStan's draw for draw: tiny rounding differences grow along a chain. Posterior means and ranges agree with Python's within the variation expected from that many draws.
Size limits on Bayesian fitting. Up to 16 chains, 100,000 warmup iterations per chain, 20,000 kept draws and 2,000,000 kept values (draws times parameters). Python has no such limits.
mcmcoptions past the first three fail with anOptionsError. A model whose kept values pass the last fails with aFittingErrorwhosereasonis"sampling-limit". See Bayesian fitting.Bayesian fits without features. Python 1.4.0's
predictfails for a model fitted withmcmc_samplesthat has no seasonalities, holidays or regressors: the posterior's placeholder coefficient comes back with the wrong shape. Here such a model forecasts normally.Bayesian fits with one changepoint. Python 1.4.0's per-path simulation (
vectorized=False) fails for a model fitted withmcmc_samplesand exactly one changepoint: CmdStan returns its changes as a flat array. Here such a model simulates normally.Bayesian fits with regressor models. Python passes
fit's arguments on to eachregressor_predictormodel. An optimizing one fails on sampling arguments, sofit(df, chains=2)fails there, and a sampling one fails on optimizer arguments such asalgorithm. Here a regressor model's sampling settings are its ownmcmc, so the main model'schainsandwarmupnever reach it, and the main model'smap.optimizeronly reaches an optimizing one.Regressor coefficient ranges.
getRegressorCoefficientsalways uses an 80% range, Python's defaultinterval_width. Python'sregressor_coefficientsuses the model's owninterval_width.Uncertainty simulation. Ranges use Prophet's per-path simulation (
vectorized=False), not Python's default shortcut. The shortcut only allows trend changes at the requested rows and assumes they are evenly spaced, but here you supply your own rows. On evenly spaced rows the two agree: in our long-history benchmark, the average trend width of future ranges was 17.7 for the shortcut and 17.8 for the per-path method over eight seeds.Size limits on uncertainty. One call takes up to 10,000 rows and 2,048 samples. Python has no such limits. With regressors forecast by their own models, the rows after the history × samples can be at most 1,000,000, because those models' draws are kept for every sample. A sampled model counts its
ceil(samples / draws) × drawsfutures.Draws from regressor models. Python draws as many samples from each regressor model as its
uncertainty_samples, 1,000 unless its options set another, and reuses them in turn when the main model draws more, all from numpy's one global generator. Each regressor model here draws as many samples as the call asks for, from its own seed derived from the call's, so the main model's draws are the same as without regressor models. A sampled regressor model runsceil(n / draws)futures per posterior draw in both, for Python'suncertainty_samplesor the call's count. Ranges agree in width: in the fixture, the average future range was 0.465 here over eight seeds and 0.461 in Python. See Uncertainty ranges for details. Becauseanomaliessimulates its whole history, it takes histories of up to 10,000 rows; see Finding anomalies for longer ones.Memory limit for Newton's method. Newton's method, used for histories under 100 rows and as L-BFGS's fallback, keeps four tables of size parameters × parameters. The engine can use at most 4 GiB of memory, so Newton can never fit more than 11,585 parameters. In practice the limit is somewhat lower, because the rest of the fit needs memory too, and lower still if your runtime gives the engine less. Each Fourier order adds two parameters, and changepoints, events and regressors add more. Past the limit,
fitfails with aFittingErrorwhosereasonis"memory-limit", withparameterCountandmemoryLimitBytes. Python has no such limit.Percentage scores near zero. When an actual value is within
1e-8of zero, Python dropsmapefromperformance_metricsand logs a message.performanceMetricsfails with anEvaluationMetricErrorwhosereasonis"zero-actual"instead, so a requested score never goes missing. Formdape, Python keeps the infinite orNaNratio an actual of exactly zero produces, and the median may or may not hide it.performanceMetricsfails the same way for that actual. A score that overflows fails with"non-finite"where Python returns infinity. See Actual values of zero.Cross-validation with regressor models. Each fold fits its regressor models to that fold's own regressor values, exactly as
fitat the cutoff would. Python'scross_validationrefits them on the model's stored history, where Prophet has already standardized the regressor values. Prophet's scaling doesn't treat a shifted series the same, so Python's fold forecasts drift from fitting at the cutoff. The gap depends on the data: about 2% in fold errors on one test series, but about 15% too high on the flat regressor-model benchmark history, where fitting at the cutoff matches the actual values. The benchmarks therefore compare against Python fitted at each cutoff.Order of forecasts with the same horizon. Python sorts forecasts by horizon with a sort that leaves equal horizons in no fixed order.
performanceMetricskeeps them in the ordercrossValidatereturns them, oldest round first. This decides the order ofrowsscores and which forecastsmdapeborrows from a shorter horizon to fill a window. It can also change the last digit of a total. Otherwise scores match Python's exactly.Warm starts are checked. Python quietly replaces a
k,morsigma_obsit can't read as a number with its default.fitrejects a warm start with a non-finite number, or asigmaObsthat isn't positive, with anOptionsError. Mismatcheddeltaandbetalengths fall back to the defaults, as in Python.Warm starts from flat models. Flat models don't keep Stan's rate and changepoint adjustments, which don't affect a flat trend, so
warmStartParametersreturnsk: 0and an emptydeltafor them, and the next fit starts the adjustments from Prophet's defaults. Python's values for these after a converged fit are close to zero.Inputs that cannot be fitted return an error. If every row has the same timestamp, there is no way to draw a trend, so
fitfails where Python returnsNaNforecasts. These are the intentional differences in the benchmarks.Last-digit differences. Fits stop when they are close enough, and rounding differs across computers, so forecasts agree to many decimal places but not always all of them. For logistic growth, the fitted internal numbers can differ slightly even when forecasts match; the benchmarks flag these cases.