Fast, dependence-aware uncertainty for time series and panels
tsbootstrap provides block, model-based, and wild resampling, confidence and
prediction intervals, and fused statistics without resampled-path tensors.
Typed method specifications and method metadata make
the sampling assumptions explicit. The same library handles a single time
series or a ragged panel of unequal-length series.
Workload |
Result |
Boundary and receipt |
|---|---|---|
Four methods shared with |
Faster in all 16 measured cells; 4.7x to 33x on the longer series |
Compiled mean reduction versus |
10,000 series, B=1,000 |
220x faster |
Fused panel mean reduction versus a per-series reduce loop, on the time axis. Panel receipt. |
Same panel |
141x less peak memory |
Fused panel reduction versus materialize-then-reduce, on the memory axis. This is a different baseline from the time comparison. |
Area |
|
Comparison boundary |
|---|---|---|
Shared methods |
IID, moving block, circular block, stationary block |
All four are in arch.bootstrap. |
Further resampling |
Non-overlapping and tapered blocks; recursive AR, ARIMA, VAR and sieve; wild and block-wild innovations |
Outside the four-method benchmark. |
Uncertainty |
Bootstrap confidence intervals, AR forecast bands, EnbPI and adaptive conformal calibration |
Outside the speed comparison. |
Scale and tooling |
Ragged-panel reduction, diagnostics, method metadata, read-only MCP tools |
The panel baselines are two other |
The headline speed figures apply to the optional compiled named-statistic
reducer. The default NumPy backend, arbitrary Python statistics, materialized
samples, and one-thread execution have separate performance profiles. See the
full benchmark methodology
and the engineering deep dive.
arch also supports independent-samples bootstrapping and has econometric
functionality beyond its bootstrap module; these are outside this comparison.
Start with bootstrap() and a typed method specification:
import numpy as np
from tsbootstrap import bootstrap, MovingBlock
rng = np.random.default_rng(0)
innovations = rng.standard_normal(200)
x = np.empty_like(innovations)
x[0] = innovations[0]
for t in range(1, len(x)):
x[t] = 0.6 * x[t - 1] + innovations[t]
result = bootstrap(x, method=MovingBlock(block_length="auto"), n_bootstraps=999, random_state=0)
samples = result.values() # shape (999, 200)
oob = result.get_oob_mask() # shape (999, 200) boolean out-of-bag mask
User guide
Tutorials
Articles
Deep dives on the statistics and engineering behind the library, with worked examples and animations:
Your bootstrap is lying to you: why the ordinary i.i.d. bootstrap collapses on autocorrelated data and how block resampling repairs it.
When your errors aren’t equal: the wild bootstrap for heteroskedastic errors.
Count the bytes, not the FLOPs: the memory-wall engineering behind the compiled backend.
Ten thousand series, one pass: fused panel reduction at scale, with separate time and memory baselines.
API reference
- bootstrap()
- Method specifications (tsbootstrap.methods)
- Results (tsbootstrap.results)
- Diagnostics (tsbootstrap.diagnostics)
- Uncertainty quantification (tsbootstrap.uq)
EnbPIEnsemblefit_predict_oob()enbpi_intervals()forecast_intervals()aci_halfwidths()nexcp_quantile()agaci_bounds()AgACIBoundsstatic_halfwidths()sliding_window_halfwidths()BaseCalibratorSpecStaticSlidingWindowACINexCPAgACIpercentile_interval()basic_interval()jackknife_statistics()block_jackknife_se()studentized_interval()jackknife_acceleration()bca_interval()conf_int()conf_int_panel()
- sktime adapters (tsbootstrap.adapters)
BaseTimeSeriesBootstrapIIDBootstrapMovingBlockBootstrapCircularBlockBootstrapStationaryBlockBootstrapNonOverlappingBlockBootstrapTaperedBlockBootstrapARResidualBootstrapARIMAResidualBootstrapVARResidualBootstrapSieveBootstrapBaseTimeSeriesBootstrapIIDBootstrapMovingBlockBootstrapCircularBlockBootstrapStationaryBlockBootstrapNonOverlappingBlockBootstrapTaperedBlockBootstrapARResidualBootstrapARIMAResidualBootstrapVARResidualBootstrapSieveBootstrap
- Errors and warnings (tsbootstrap.errors)