FAQ¶
Installation and setup¶
Prophet is hanging on first import. What is happening?
Prophet compiles a Stan model the first time it is imported. This compilation step
takes approximately 5–10 minutes and only happens once per environment. Subsequent
imports are fast. If the hang persists beyond 15 minutes, check that CmdStan installed
correctly: import cmdstanpy; cmdstanpy.cmdstan_path().
I get an XGBoost error about libomp.dylib not loaded on macOS with conda.
This affects Intel Mac users who install the package via the conda path in the
Setup guide. The conda environment uses pip install -e . to
install dependencies, and the pip-distributed XGBoost binary on Intel Macs links
against OpenMP at a Homebrew path (/usr/local/opt/libomp/) that conda environments
cannot see. Running brew install libomp does not fix it — Homebrew and conda manage
libraries independently, and the library ends up in a location the conda environment
never searches.
After hitting the error, replace the pip-installed XGBoost with the conda-forge build, which bundles OpenMP internally:
If that does not resolve it, install OpenMP directly into the conda environment:
Note: this issue does not affect Apple Silicon (M-series) Macs or users following the
venv-based installation path.
I get an ImportError for NeuralProphet or PyTorch.
NeuralProphet is an optional dependency (~1 GB including PyTorch) and is not installed by default. Install it with:
I get an error about pkg_resources not found.
The package pins setuptools>=68,<71 because NeuralProphet uses pkg_resources,
which was removed in setuptools 71+. Check your setuptools version:
If it is 71 or higher, downgrade: pip install "setuptools>=68,<71".
Running the pipeline¶
How do I know if my counterfactual is credible?
Two checks in order of importance:
-
Visual: open
{model}_counterfactual.pngand inspect the pre-event period. The observed and expected lines should track closely before the event. Divergence before the event means the model did not capture the baseline trend. -
Numeric: check
{model}_metrics.csv. A test MASE near or above 1 (no better than carrying forward the last seasonal cycle) or a test RMSE that is large relative to the outcome's range signals poor generalization. The test window performance — not training performance — is the relevant signal.
If either check fails, try a different model via compare_models() or add covariates
that help explain the baseline trend.
What block length should I use for the Moving Block Bootstrap?
The default block_length=14 is appropriate for daily data with moderate
autocorrelation (approximately two weeks of temporal dependence). Block length is
measured in observations, never calendar days -- 14 means 14 weeks on a weekly
series -- and it is not automatically adapted to other data configurations.
For non-daily data or series with substantially different autocorrelation structure,
the default may produce CI coverage that is too narrow or too wide. Adjust via
config_overrides={"bootstrap": {"block_length": <value>}}. Automated block length
selection is not currently implemented.
My bootstrap is producing many "simulation failure" warnings.
Warnings about failed bootstrap simulations typically mean the model failed to converge on some resampled series. Common causes:
- Training window is too short for reliable refitting on bootstrap resamples.
- The series contains extreme outliers that cause numerical instability.
- NeuralProphet training stochasticity on short windows.
If more than ~10% of simulations fail (the package warns at 50%), the CI coverage may be unreliable. Check whether the full series has enough data and whether outliers should be handled before running.
Can I use this package on weekly or monthly data?
Yes. Frequency-dependent defaults resolve automatically, with two caveats:
- ARIMA's seasonal period
mdefaults to"auto"and resolves from the series frequency (daily 7, weekly 52, monthly 12). An explicit override (config_overrides={"models": {"arima": {"m": 52}}}) is needed only for frequencies outside that mapping (e.g. quarterly), where auto falls back tom=1with a warning. Be aware of the runtime cost: on weekly data the resolvedm=52seasonal search can be substantially slower; set an explicit smallermorseasonal: falseto trade seasonality for speed. - Series frequency itself is resolved automatically from the date column; the series must be a complete, regularly spaced grid (no gaps or duplicate dates), or the pipeline raises an error naming the first offending timestamp.
- Consider adjusting
block_lengthfor the MBB (default 14 is calibrated for daily data; block length is measured in observations, so 14 means 14 weeks on a weekly series). - NeuralProphet's
n_lagsis likewise an observation count, not days: the default 14 is a 14-week autoregressive window on a weekly series. Consider whether that window is what you mean.
What is the minimum series length?
There is no hard minimum, but the series must be long enough to:
- Support the cross-validation framework (
min_train_obs+ at least one fold oftest_obs+skip_obs-- all observation counts, not calendar days). - Provide a reliable test window (default 20% of pre-intervention observations) for model selection.
Series shorter than about two years of daily observations will typically produce unstable tuning and cross-validation results. Shorter series are better served by simpler models (ARIMA) with reduced CV requirements.
Data¶
My covariate is missing in part of the post-event window.
Covariates must be present for every row, including the entire post-event projection window. Options:
- Shorten
holdout_daysto the period for which the covariate is available. - Drop the covariate if it cannot be extended.
- Impute the post-event covariate externally (only appropriate if the imputed values would not be affected by the event itself).
I have missing values in my outcome column.
By default, the pipeline raises a ValueError on missing outcome values. Two
automated strategies are available:
# drop rows with missing outcome
run_single_its(df, ..., config_overrides={"data": {"missing_data": "drop"}})
# linear interpolation
run_single_its(df, ..., config_overrides={"data": {"missing_data": "interpolate"}})
For models sensitive to regular spacing (ARIMA, NeuralProphet), external imputation with explicit handling is preferred.
How do I switch to date-based splitting?
The default split_method="percent" sizes test/holdout windows as fractions of the
available data. To pin the windows to fixed calendar durations instead:
run_single_its(
df, intervention_date="2022-06-01",
config_overrides={
"periods": {
"split_method": "days",
"test_days": 365,
"holdout_days": 365,
},
},
)
In "days" mode, windows are calendar time whatever the series frequency: on weekly
data test_days=365 spans about 52 observations. test_days must be strictly less
than the calendar span of the pre-intervention data or the pipeline raises
ValueError (a previously silent failure mode where the training split came back
empty). To pin windows to exact observation counts instead, use
split_method="observations" with test_obs / holdout_obs (both required, no
defaults). Arguments belonging to a different split_method raise an error rather
than being silently ignored.
Configuration¶
What is the config_overrides format?
config_overrides is a nested dict that mirrors the structure of params.yaml. It is
merged on top of the package defaults at the highest priority:
config_overrides = {
"periods": {"split_method": "days", "test_days": 180, "holdout_days": 90},
"bootstrap": {"n_sim": 500, "block_length": 14},
"models": {"arima": {"m": 52}},
}
Inside tune_model, the search space also accepts double-underscore flattened keys
for nested model parameters (e.g., "xgb__max_depth" for XGBoost's max_depth).
Why did my yearly seasonality disappear (or appear) when I changed the date range?
The Prophet-backed models ship yearly_seasonality: auto, and both Prophet and
NeuralProphet resolve auto with the same rule: the yearly component is enabled only
when the training history spans at least 730 days (two annual cycles). This is a hard
boundary, not a gradual taper. A training window spanning 729 days gets no yearly
component; one spanning 730 days gets a full one, at Fourier order 10.
That matters most for a common setup: roughly two years of daily pre-period data. If your series has genuine annual structure and the component is disabled, the seasonal signal does not disappear -- it loads onto the trend instead, and the counterfactual forecast extrapolates that contaminated trend. The estimated effect absorbs the season, biased in whichever direction the season was moving at the intervention.
its2s therefore reports the resolution in BOTH directions, as a UserWarning naming
the observed span, the rule, and the override, so you can always see which side of
the boundary your series landed on. Override the rule explicitly when you know your
series better than the day count does:
An explicit True or False is honored silently -- the report is only for auto.
Setting it explicitly is the right move whenever your training window sits near 730
days, in either direction.