Skip to content

ENH: Add the local block bootstrap - #967

Open
bashtage wants to merge 2 commits into
mainfrom
a91-f617-local-block-bootstrap
Open

bashtage wants to merge 2 commits into
mainfrom
a91-f617-local-block-bootstrap

Conversation

@bashtage

Copy link
Copy Markdown
Owner

Target 9.1: merge after v9.0.0 is tagged. Closes #617.

Adds arch.bootstrap.LocalBlockBootstrap(block_size, locality, *args, seed=None, **kwargs), the local block bootstrap of Paparoditis and Politis (2002), C. R. Acad. Sci.
Paris, Ser. I
335(11), 959-962, for locally stationary series.

Design:

  • Each block's start is drawn uniformly on [max(0, m·b − K), min(n − b, m·b + K)], with K
    the locality in observations (the paper's nB). The blocks are concatenated and
    truncated to n.
  • update_indices is vectorised NumPy (independent draws per block), with no compiled
    sampler.
  • It subclasses CircularBlockBootstrap. locality is a read-only property like
    block_size, and clone keeps both. _check_sampling requires block_size <= n.
  • With K >= n, every start is allowed, which is the moving block bootstrap.
  • apply, cov, var, every conf_int method, and seed and state handling work
    unchanged.
  • It is not offered in SPA, StepM or MCS. Their null assumes stationarity, and a test
    checks that "local" is rejected there.
  • The docs cover the purpose, the reference and the 0-based convention (the paper is
    1-based). The caveats are:
    • BCa's jackknife is heuristic;
    • the studentized interval reuses b and K, and std_err_func is the alternative;
    • choosing b and K is left to the user.

Validation:

  • No package implements the method; all 25,424 CRAN packages were searched. The reference
    is an independent base-R implementation of the paper: the indices are identical on 8
    cases, which are stored in the tests.
  • A Python loop reference agrees on 20 cases, with both Generator and RandomState draws.
  • For a trend plus noise (n = 600, b = 10, K = 40), the standard error of the mean is
    0.0424 against the true 0.0408. The circular block bootstrap gives 0.11-0.12.

Tests:

  • test_local_block_bootstrap.py (186): exact support on edge cases; chi-square
    uniformity of the starts; the trend check; clone, seed, state and the read-only
    properties; every conf_int method, including the nested studentized interval; pandas
    and keyword inputs.
  • test_validation_local_block_bootstrap.py (232 rows).
  • Deliberate bugs are each caught: an exclusive upper end (the prototype's bug), the
    literal 1-based window, no truncation, and clone dropping the locality.
  • Fast suite 27,385 passed. The tests pass in random orders and without matplotlib.
    numpydoc lint 0, and the docs build has no new warnings.

Thanks to @yngvem for the proposal and the prototype.

🤖 Generated with Claude Code

Add LocalBlockBootstrap(block_size, locality, *args, seed=None, **kwargs), the
local block bootstrap of Paparoditis and Politis (2002) for series whose
properties change slowly. Block m starts at an integer drawn uniformly from
[max(0, m * block_size - locality), min(n - block_size, m * block_size + locality)]
and the blocks are truncated to n observations. The locality is an integer
number of observations so that clone works with data of any length.

The class subclasses CircularBlockBootstrap for the block_size property, str and
repr, and adds a read-only locality, clone that keeps both parameters, and a
vectorised update_indices. It is not an option of SPA, StepM or MCS.

xref #617
Check the block starts against loop implementations of the algorithm of the paper
(in Python and in R) driven by the same uniform draws, the exact support of the
starts in edge cases, the uniformity and independence of the starts, the standard
error of the mean of a trend plus noise, seed, state, reset and clone, and apply,
cov, var and every conf_int method against a replay of the reference. Table-driven
validation rows cover locality, block_size, seed, the data and the method arguments.

xref #617
Comment thread arch/bootstrap/base.py
checked = check_int("locality", locality, lower=0)
super().__init__(block_size, *args, seed=seed, **kwargs)
self._locality: int = checked
self._parameters = [self._block_size, checked]
@codecov

codecov Bot commented Oct 18, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.97%. Comparing base (d2c90b4) to head (207aa90).
⚠️ Report is 6 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #967      +/-   ##
==========================================
+ Coverage   98.95%   98.97%   +0.02%     
==========================================
  Files         136      138       +2     
  Lines       30043    30713     +670     
  Branches     2331     2375      +44     
==========================================
+ Hits        29729    30399     +670     
  Misses        256      256              
  Partials       58       58              
Flag Coverage Δ
adder 98.92% <100.00%> (+0.02%) ⬆️
subtractor 98.92% <100.00%> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Local block bootstrap

2 participants