Uncertainty quantification guide
The bootstrap produces resampled series; the uq layer turns those into
prediction intervals. There are two paths, plus a family of calibrators that
control how interval width is computed.
The coverage here is approximate or asymptotic under temporal dependence, not finite-sample distribution-free. Read the per-method note before relying on a number.
Classical confidence intervals
conf_int() gives a confidence interval for a statistic of one
series in a single call. It runs one bootstrap pass with the method spec you pass
and reads the requested interval family off the replicate statistics.
import numpy as np
from tsbootstrap import IID, conf_int
x = np.random.default_rng(0).standard_normal(100)
lower, upper, point = conf_int(x, "mean", method=IID(), kind="bca", alpha=0.1)
The statistic is a built-in name ("mean", "var", "std"), a
("quantile", q) tuple, or a callable, exactly as for
bootstrap_reduce(). If you have already run
bootstrap_reduce you do not need to re-run it: the low-level functions
(percentile_interval(), basic_interval(),
studentized_interval(), bca_interval())
take a replicate statistics array directly.
Four interval families are available, differing in what they assume and how fast their coverage error shrinks:
Interval |
Assumptions |
Correctness order |
Availability by method family |
|---|---|---|---|
|
only the replicate distribution |
first order, |
all method specs |
|
only the replicate distribution (reflected through the point estimate) |
first order, |
all method specs |
|
a dependence-aware per-replicate standard error |
second order, |
all method specs |
|
a jackknife acceleration defined under independent sampling |
second order, |
|
Why BCa refuses dependent specs
BCa adjusts the endpoint quantiles for median bias and skewness. Its acceleration
constant is a delete-one jackknife estimate, which is defined under independent
sampling (Efron 1987). That construction has no valid dependent-data form, so
conf_int refuses kind="bca" for any block or model method and raises a
typed MethodConfigError pointing at the studentized
interval. The dependent-data second-order route is the studentized block
bootstrap (Gotze and Kunsch 1996, Annals of Statistics 24(5), Section 3), a
structurally different interval whose acceleration comes from block-bootstrap
cumulants rather than any jackknife. R’s boot package likewise refuses BCa
for time-series bootstraps.
The studentized interval under dependence
The studentized (bootstrap-t) interval pivots each replicate by its own standard
error. To stay valid under temporal dependence, that per-replicate standard error
must itself be dependence-aware, so conf_int estimates it with a
delete-a-group block jackknife (block_jackknife_se(), Kunsch
1989): the observations are split into non-overlapping blocks and the statistic is
recomputed with each block deleted. The block length follows the method spec, or
the Politis-White rule on the series when the spec length is automatic, or an
explicit se_block_length override.
One honesty caveat carried by the literature: second-order correctness under
dependence holds only with a bias-matched variance estimator (Gotze and Kunsch
1996 require rectangular lag-window weights, not triangular ones). The
delete-a-group block jackknife is a consistent, RNG-free, deterministic estimator
of that variance and reduces exactly to the classic delete-one jackknife at block
length 1; it is documented here as a reasonable default rather than as a
guaranteed bias-matched choice for every statistic. Supply your own
se_statistic when you need a specific variance estimator.
Compiled backend
backend="compiled" accelerates the percentile and basic families with
a built-in string statistic. The studentized and bca families evaluate a
Python callable per replicate (the jackknife and the standard-error reducer), which
the compiled kernel cannot run, so they require backend="numpy" and raise a
typed error otherwise.
Confidence intervals over a panel
conf_int_panel() computes per-series intervals over a ragged
panel in one pass, returning arrays with a leading num_series axis. It builds
on bootstrap_reduce_panel(), so it accepts the
observation-resampling methods only and offers percentile, basic, and
studentized (not bca, which is gated to single-series IID data). The
studentized panel path needs an explicit block length, either se_block_length
or an integer block length on the method spec: a replicate reducer sees one series
at a time without its identity, so a per-series automatic block length cannot be
resolved honestly.
import numpy as np
from tsbootstrap import MovingBlock, conf_int_panel
rng = np.random.default_rng(0)
panel = [rng.standard_normal(n) for n in (120, 200, 150)]
lower, upper, point = conf_int_panel(
panel, "mean", method=MovingBlock(block_length=10), kind="studentized",
alpha=0.1, se_block_length=10, random_state=0,
)
Regression prediction intervals: EnbPI
EnbPIEnsemble is a fit/predict object for an
sklearn-style regressor. It bootstraps the row indices, fits a clone of the
estimator on each resample, and uses the out-of-bag ensemble residuals as the
nonconformity scores (Xu and Xie 2021). Because the clones are retained, it can
produce intervals for new X as well as in-sample.
from sklearn.linear_model import LinearRegression
from tsbootstrap import EnbPIEnsemble, MovingBlock, SlidingWindow
ens = EnbPIEnsemble().fit(
LinearRegression(), X, y, method=MovingBlock(block_length=10),
n_bootstraps=100, random_state=0,
)
lower, upper, point = ens.predict_interval(alpha=0.1, calibrator=SlidingWindow())
EnbPI requires an observation-resampling method (the block or IID families);
recursive model methods have no out-of-bag set and are rejected. The thin
wrappers enbpi_intervals() and
fit_predict_oob() cover the simple in-sample,
static-width path.
Calibrators: choosing the half-width
The interval endpoints are computed from the residual buffer by a calibrator,
selected with a frozen spec from tsbootstrap.uq.calibrators,
predict_interval(calibrator=SomeSpec(...)). Because each option is a typed
field with extra="forbid", a misspelled option fails at spec construction
rather than being silently dropped:
Static: one global quantile, the same width everywhere. Use when residuals are stationary.SlidingWindow: a rolling quantile over recent residuals (acceptswindow). Use under volatility clustering, where width should track local scale.ACI: adaptive conformal inference (Gibbs and Candes 2021). Adjusts the target level online from realized coverage errors, so long-run coverage holds under distribution shift. Needs the realized scores passed astest_dataand accepts agammalearning rate.NexCP: nonexchangeable conformal (Barber et al. 2023). A recency-weighted quantile (acceptsdecay), so recent residuals count more; carries a finite-sample guarantee minus a drift gap.AgACI: aggregated adaptive conformal inference (Zaffran et al. 2022). Aggregates a grid of ACI experts into asymmetric bounds; needs the SIGNED realized residuals passed astest_data. The +inf sentinel for a degenerate expert is data-adaptive, so coverage stays correct even for target series with magnitudes above ~1e6.
The realized test_data (the ACI scores or the AgACI signed residuals) is a
runtime argument, not a spec field, because it is data rather than configuration.
Forecast intervals
forecast_intervals() simulates a fitted model
forward over a horizon and reads per-step empirical quantiles.
from tsbootstrap import AR, forecast_intervals
lower, upper, median = forecast_intervals(
x, model=AR(order=2), horizon=12, alpha=0.1, random_state=0,
)
It supports the AR model only in this release;
ARIMA and VAR forecast intervals are planned for a later release.
Scaling calibration: bootstrap_reduce
For very large numbers of replicates, bootstrap_reduce()
evaluates a per-replicate statistic inside the chunk loop and keeps only the
reduced values, so peak memory stays proportional to the number of replicates
rather than the full path size. It enables large-sample conformal calibration
without materializing every path.