Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Local block bootstrap #617

Open
yngvem opened this issue Sep 29, 2022 · 0 comments
Open

Local block bootstrap #617

yngvem opened this issue Sep 29, 2022 · 0 comments

Comments

@yngvem
Copy link

yngvem commented Sep 29, 2022

One feature I think would be useful is bootstrap methods for nonstationary time-series. I am currently looking at the Local block bootstrap (LBB), which was relatively straightforward to implement.

The LBB works by assuming that the time-series is almost stationary, but with slowly changing properties (e.g. the seasons in a year). Then, it creates bootstrap samples by sampling blocks near each other and stitching them together.

I have created a prototype, which seems to work, but I don't have any unit tests yet.

import numpy as np
from arch.bootstrap.base import IIDBootstrap, _get_random_integers, ArrayLike, RandomState, Generator, Int64Array


class LocalBlockBootstrap(IIDBootstrap):
    _name = "Local Block Bootstrap"

    def __init__(
        self,
        block_size: int,
        max_step: int,
        *args: ArrayLike,
        random_state: RandomState | None = None,
        seed: None | int | Generator | RandomState = None,
        **kwargs: ArrayLike,
    ) -> None:
        super().__init__(*args, random_state=random_state, seed=seed, **kwargs)
        self.block_size: int = block_size
        self.max_step: int = max_step
        self._parameters = [block_size, max_step]

    def clone(
        self,
        *args: ArrayLike,
        seed: None | int | Generator | RandomState = None,
        **kwargs: ArrayLike,
    ) -> 'LocalBlockBootstrap':

        block_size = self._parameters[0]
        max_step = self._parameters[1]
        return self.__class__(block_size, max_step, *args, random_state=None, seed=seed, **kwargs)

    def update_indices(self) -> Int64Array:
        num_blocks = self._num_items // self.block_size
        if num_blocks * self.block_size < self._num_items:
            num_blocks += 1

        m = np.arange(num_blocks)
        lower = np.maximum(0, self.block_size * m - self.max_step - 1)
        upper = np.minimum(self._num_items - self.block_size, self.block_size * m + self.max_step - 1)
        step = upper - lower
        indices = lower + _get_random_integers(self.generator, step, size=len(step))

        indices = indices[:, np.newaxis] + np.arange(self.block_size)[np.newaxis, :]
        indices = indices.flatten()

        if indices.shape[0] > self._num_items:
            return indices[: self._num_items]
        else:
            return indices
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

No branches or pull requests

1 participant