Skip to content

BootstrapGWR

This page documents 1 public symbols. Each entry includes its purpose, import path, full API docstring, and the maintained example that exercises it.

Conceptual guide

BootstrapGWR

Test GWR coefficient non-stationarity by parametric bootstrap.

Property Value
Type class
Import from pygwrx.models import BootstrapGWR
Signature BootstrapGWR(bandwidth: 'Union[float, int, str, None]' = 'aicc', adaptive: 'bool' = False, kernel: 'str' = 'bisquare', bandwidth_method: 'str' = 'aicc', bandwidth_range: 'Optional[Tuple[float, float]]' = None, optimization_method: 'str' = 'golden_section', fit_intercept: 'bool' = True, distance_metric: 'str' = 'euclidean', n_bootstrap: 'int' = 99, reselect_bandwidth: 'bool' = True, pvalue_method: 'str' = 'plus_one', localized_tail: 'str' = 'two-sided', store_local_bootstrap: 'bool' = False, random_state: 'Optional[Union[int, np.random.Generator]]' = None, verbose: 'bool' = False) -> 'None'
Maintained example examples/models/14_bootstrap_gwr.py

BootstrapGWR

BootstrapGWR(
    bandwidth: Union[float, int, str, None] = "aicc",
    adaptive: bool = False,
    kernel: str = "bisquare",
    bandwidth_method: str = "aicc",
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    n_bootstrap: int = 99,
    reselect_bandwidth: bool = True,
    pvalue_method: str = "plus_one",
    localized_tail: str = "two-sided",
    store_local_bootstrap: bool = False,
    random_state: Optional[Union[int, Generator]] = None,
    verbose: bool = False,
)

Test GWR coefficient non-stationarity by parametric bootstrap.

For coefficient :math:j, the modified statistic is the sample standard deviation across locations of the fitted GWR pseudo-t values,

.. math::

T_j = \operatorname{sd}_i\left(\hat\beta_j(s_i) / \widehat{se}_j(s_i)\right).

The localised statistic compares each local coefficient with the matching coefficient from the global null model,

.. math::

t_{ij}^{\mathrm{loc}} =
\frac{\hat\beta_j(s_i)-\hat\beta_j^{\mathrm{OLS}}}
     {\widehat{se}_j(s_i)}.

Bootstrap responses are generated parametrically under the OLS null as X @ beta_ols + Normal(0, sigma_ols). By default, an automatically selected GWR bandwidth is selected again in every bootstrap replicate, as in GWmodel. Set reselect_bandwidth=False to condition on the observed selected bandwidth.

Parameters:

Name Type Description Default
bandwidth Union[float, int, str, None]

Numeric GWR bandwidth, automatic criterion ("cv", "aic", "aicc", or "bic"), or None.

'aicc'
adaptive bool

Interpret a numeric or selected bandwidth as a neighbour count.

False
kernel str

GWR spatial kernel.

'bisquare'
bandwidth_method str

Criterion used when bandwidth=None.

'aicc'
bandwidth_range Optional[Tuple[float, float]]

Optional search interval for automatic bandwidth selection.

None
optimization_method str

Bandwidth search method forwarded to :class:GWR.

'golden_section'
fit_intercept bool

Include an intercept in both GWR and OLS null models.

True
distance_metric str

Distance metric forwarded to :class:GWR.

'euclidean'
n_bootstrap int

Number of parametric bootstrap replicates.

99
reselect_bandwidth bool

Re-select an automatic bandwidth in each replicate.

True
pvalue_method str

"plus_one" uses the finite-sample correction (1 + exceedances) / (R + 1). "gwmodel" reproduces the maintained R helper exceedances / (R + 1).

'plus_one'
localized_tail str

"two-sided" compares absolute pseudo-t statistics; "right" reproduces the one-sided comparison in GWmodel.

'two-sided'
store_local_bootstrap bool

Store the full R x n x p local statistic array. Local p-values are available regardless of this option.

False
random_state Optional[Union[int, Generator]]

Seed or NumPy generator for reproducible simulation.

None
verbose bool

Print bootstrap progress.

False

Attributes:

Name Type Description
modified_statistics_

Observed coefficient-wise modified statistics.

modified_p_values_

Bootstrap right-tail p-values for the modified test.

localized_statistics_

Observed n x p localised pseudo-t statistics.

localized_p_values_

Observation- and coefficient-specific bootstrap p-values.

coefficients_gwr_

Full local parameter matrix including the intercept.

coefficients_global_

OLS null-model parameter vector.

bandwidth_

Bandwidth selected for the observed GWR fit.

References

Harris, P., Brunsdon, C., Lu, B., Nakaya, T., & Charlton, M. (2017). Introducing bootstrap methods to investigate coefficient non-stationarity in spatial regression models. Spatial Statistics, 21, 241-261. https://doi.org/10.1016/j.spasta.2017.07.006

Source code in src/pygwrx/models/bootstrap_gwr.py
def __init__(
    self,
    bandwidth: Union[float, int, str, None] = "aicc",
    adaptive: bool = False,
    kernel: str = "bisquare",
    bandwidth_method: str = "aicc",
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    n_bootstrap: int = 99,
    reselect_bandwidth: bool = True,
    pvalue_method: str = "plus_one",
    localized_tail: str = "two-sided",
    store_local_bootstrap: bool = False,
    random_state: Optional[Union[int, np.random.Generator]] = None,
    verbose: bool = False,
) -> None:
    self.bandwidth = bandwidth
    self.adaptive = adaptive
    self.kernel = kernel
    self.bandwidth_method = bandwidth_method
    self.bandwidth_range = bandwidth_range
    self.optimization_method = optimization_method
    self.fit_intercept = fit_intercept
    self.distance_metric = distance_metric
    self.n_bootstrap = n_bootstrap
    self.reselect_bandwidth = reselect_bandwidth
    self.pvalue_method = pvalue_method
    self.localized_tail = localized_tail
    self.store_local_bootstrap = store_local_bootstrap
    self.random_state = random_state
    self.verbose = verbose
    self._reset_fit_state()

fit

fit(
    X: Union[ndarray, DataFrame],
    y: Union[ndarray, Series],
    coords: Union[ndarray, DataFrame],
) -> "BootstrapGWR"

Fit the observed models and run the MLR-null parametric bootstrap.

Source code in src/pygwrx/models/bootstrap_gwr.py
def fit(
    self,
    X: Union[np.ndarray, pd.DataFrame],
    y: Union[np.ndarray, pd.Series],
    coords: Union[np.ndarray, pd.DataFrame],
) -> "BootstrapGWR":
    """Fit the observed models and run the MLR-null parametric bootstrap."""
    self._reset_fit_state()
    self._validate_parameters()
    try:
        X_arr, y_arr, coords_arr, names = self._validate_data(X, y, coords)
        X_design = add_intercept(X_arr) if self.fit_intercept else X_arr.copy()
        parameter_names = (("intercept",) if self.fit_intercept else ()) + tuple(
            str(name)
            for name in (
                names
                if names is not None
                else [f"x{i}" for i in range(X_arr.shape[1])]
            )
        )

        observed_gwr = self._make_gwr(self.bandwidth)
        observed_gwr.fit(
            X_arr,
            y_arr,
            coords_arr,
            compute_hat_matrix=False,
            compute_local_r2=False,
            compute_inference=True,
        )
        local_params, local_se = self._full_local_parameters(observed_gwr)
        global_beta, global_fitted, global_residuals, global_rss, global_sigma = (
            self._fit_ols(X_design, y_arr)
        )
        modified, local_t = self._modified_statistic(local_params, local_se)
        localized = self._localized_statistic(local_params, global_beta, local_se)

        n_parameters = X_design.shape[1]
        bootstrap_modified = np.empty((self.n_bootstrap, n_parameters), dtype=float)
        bootstrap_local = (
            np.empty((self.n_bootstrap, X_arr.shape[0], n_parameters), dtype=float)
            if self.store_local_bootstrap
            else None
        )
        modified_exceedances = np.zeros(n_parameters, dtype=np.int64)
        localized_exceedances = np.zeros(
            (X_arr.shape[0], n_parameters), dtype=np.int64
        )
        bootstrap_bandwidths = np.empty(self.n_bootstrap, dtype=float)
        rng = self._rng()
        bandwidth_spec = (
            self.bandwidth if self.reselect_bandwidth else observed_gwr.bandwidth_
        )
        tail = str(self.localized_tail).strip().lower()

        for index in range(self.n_bootstrap):
            y_boot = global_fitted + rng.normal(
                loc=0.0, scale=global_sigma, size=X_arr.shape[0]
            )
            boot_gwr = self._make_gwr(bandwidth_spec)
            boot_gwr.fit(
                X_arr,
                y_boot,
                coords_arr,
                compute_hat_matrix=False,
                compute_local_r2=False,
                compute_inference=True,
            )
            boot_params, boot_se = self._full_local_parameters(boot_gwr)
            boot_global_beta, _, _, _, _ = self._fit_ols(X_design, y_boot)
            boot_modified, _ = self._modified_statistic(boot_params, boot_se)
            boot_localized = self._localized_statistic(
                boot_params, boot_global_beta, boot_se
            )

            bootstrap_modified[index] = boot_modified
            if bootstrap_local is not None:
                bootstrap_local[index] = boot_localized
            modified_exceedances += boot_modified >= modified
            if tail == "two-sided":
                localized_exceedances += np.abs(boot_localized) >= np.abs(localized)
            else:
                localized_exceedances += boot_localized > localized
            bootstrap_bandwidths[index] = float(boot_gwr.bandwidth_)

            if self.verbose:
                step = max(1, self.n_bootstrap // 10)
                if (index + 1) % step == 0 or index + 1 == self.n_bootstrap:
                    print(
                        f"BootstrapGWR: completed {index + 1}/{self.n_bootstrap} replicates."
                    )

        modified_p = self._bootstrap_p_values(modified_exceedances)
        localized_p = self._bootstrap_p_values(localized_exceedances)

        self.n_samples_ = X_arr.shape[0]
        self.n_features_in_ = X_arr.shape[1]
        self.feature_names_in_ = None if names is None else names.copy()
        self.parameter_names_ = parameter_names
        self.bandwidth_ = observed_gwr.bandwidth_
        self.gwr_model_ = observed_gwr
        self.coefficients_gwr_ = local_params
        self.local_standard_errors_ = local_se
        self.local_t_values_ = local_t
        self.fitted_values_gwr_ = np.asarray(
            observed_gwr.fitted_values_, dtype=float
        )
        self.residuals_gwr_ = np.asarray(observed_gwr.residuals_, dtype=float)
        self.coefficients_global_ = global_beta
        self.fitted_values_global_ = global_fitted
        self.residuals_global_ = global_residuals
        self.sigma_global_ = global_sigma
        self.modified_statistics_ = modified
        self.modified_critical_values_ = np.quantile(
            bootstrap_modified, 0.95, axis=0
        )
        self.modified_p_values_ = modified_p
        self.bootstrap_modified_statistics_ = bootstrap_modified
        self.localized_statistics_ = localized
        self.localized_p_values_ = localized_p
        self.bootstrap_localized_statistics_ = bootstrap_local
        if bootstrap_local is not None:
            self.localized_lower_critical_ = np.quantile(
                bootstrap_local, 0.025, axis=0
            )
            self.localized_upper_critical_ = np.quantile(
                bootstrap_local, 0.975, axis=0
            )
        self.bootstrap_bandwidths_ = bootstrap_bandwidths
        self.rss_gwr_ = float(np.dot(self.residuals_gwr_, self.residuals_gwr_))
        self.rss_global_ = global_rss
        self.trace_S_ = float(observed_gwr.diagnostics_["trace_S"])

        # Compatibility aliases from the historical pyGWRx API.  The valid
        # Harris/GWmodel statistic is coefficient-wise rather than scalar.
        self.test_statistic_ = self.modified_statistics_.copy()
        self.bootstrap_statistics_ = self.bootstrap_modified_statistics_.copy()
        self.p_value_ = self.modified_p_values_.copy()
        self.X_train_ = X_arr.copy()
        self.y_train_ = y_arr.copy()
        self.coords_train_ = coords_arr.copy()
        self._is_fitted = True
        return self
    except Exception:
        self._reset_fit_state()
        raise

to_frame

to_frame() -> pd.DataFrame

Return local coefficients, inference, and bootstrap p-values.

Source code in src/pygwrx/models/bootstrap_gwr.py
def to_frame(self) -> pd.DataFrame:
    """Return local coefficients, inference, and bootstrap p-values."""
    self._check_is_fitted()
    if (
        self.coords_train_ is None
        or self.coefficients_gwr_ is None
        or self.local_standard_errors_ is None
        or self.localized_statistics_ is None
        or self.localized_p_values_ is None
        or self.parameter_names_ is None
    ):
        raise RuntimeError("Stored BootstrapGWR results are incomplete.")
    data: Dict[str, np.ndarray] = {
        "coord_0": self.coords_train_[:, 0],
        "coord_1": self.coords_train_[:, 1],
    }
    for index, name in enumerate(self.parameter_names_):
        data[f"coef_{name}"] = self.coefficients_gwr_[:, index]
        data[f"se_{name}"] = self.local_standard_errors_[:, index]
        data[f"localized_t_{name}"] = self.localized_statistics_[:, index]
        data[f"localized_p_{name}"] = self.localized_p_values_[:, index]
    return pd.DataFrame(data)

summary

summary() -> str

Return a plain-text summary of the bootstrap test.

Source code in src/pygwrx/models/bootstrap_gwr.py
def summary(self) -> str:
    """Return a plain-text summary of the bootstrap test."""
    self._check_is_fitted()
    return format_summary(
        "Bootstrap GWR Summary",
        {
            "parameter_names": self.parameter_names_,
            "bandwidth": self.bandwidth_,
            "n_bootstrap": self.n_bootstrap,
            "null_model": "ols",
            "modified_statistics": self.modified_statistics_.copy(),
            "modified_critical_values_95": self.modified_critical_values_.copy(),
            "modified_p_values": self.modified_p_values_.copy(),
            "rss_gwr": self.rss_gwr_,
            "rss_global": self.rss_global_,
            "trace_S": self.trace_S_,
            "reselect_bandwidth": self.reselect_bandwidth,
            "pvalue_method": self.pvalue_method,
            "localized_tail": self.localized_tail,
        },
    )

Runnable examples used on this page

examples/models/14_bootstrap_gwr.py
# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Run coefficient-wise bootstrap tests for spatial variability."""

# Allow this script to run directly from any working directory.
import sys
from pathlib import Path

_PROJECT_ROOT = Path(__file__).resolve().parents[2]
_EXAMPLES_ROOT = _PROJECT_ROOT / "examples"
_SRC_ROOT = _PROJECT_ROOT / "src"
for _path in (_SRC_ROOT, _EXAMPLES_ROOT):
    if str(_path) not in sys.path:
        sys.path.insert(0, str(_path))

from _common import print_model_result, spatial_regression

from pygwrx import BootstrapGWR

X, y, coords = spatial_regression(n=42, p=2)
model = BootstrapGWR(
    bandwidth=22,
    adaptive=True,
    n_bootstrap=9,
    reselect_bandwidth=False,
    store_local_bootstrap=True,
    random_state=0,
).fit(X, y, coords)
print_model_result(model)
print("modified_pvalues=", model.modified_p_values_)
print("localized_p_values_shape=", model.localized_p_values_.shape)