Skip to content

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:

conda install -c conda-forge xgboost

If that does not resolve it, install OpenMP directly into the conda environment:

conda install -c conda-forge "libcxx<17"

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:

pip install its2s[neural]

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:

pip show setuptools

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:

  1. Visual: open {model}_counterfactual.png and 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.

  2. 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 m defaults 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 to m=1 with a warning. Be aware of the runtime cost: on weekly data the resolved m=52 seasonal search can be substantially slower; set an explicit smaller m or seasonal: false to 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_length for 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_lags is 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:

  1. Support the cross-validation framework (min_train_obs + at least one fold of test_obs + skip_obs -- all observation counts, not calendar days).
  2. 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_days to 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:

config_overrides = {
    "models": {"prophet_xgb": {"prophet": {"yearly_seasonality": True}}},
}

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.