Skip to content

GWR

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

Conceptual guide

GWR

Gaussian geographically weighted regression.

Property Value
Type class
Import from pygwrx.models import GWR
Signature GWR(kernel: 'Union[str, Callable[[np.ndarray, float], np.ndarray]]' = 'gaussian', bandwidth: 'Union[float, int, str, None]' = 'cv', bandwidth_method: 'str' = 'cv', adaptive: 'bool' = False, bandwidth_range: 'Optional[Tuple[float, float]]' = None, optimization_method: 'str' = 'golden_section', fit_intercept: 'bool' = True, distance_metric: 'str' = 'euclidean', sigma2_v1: 'bool' = True, verbose: 'bool' = False) -> 'None'
Maintained example examples/models/01_gwr.py

GWR

GWR(
    kernel: Union[
        str, Callable[[ndarray, float], ndarray]
    ] = "gaussian",
    bandwidth: Union[float, int, str, None] = "cv",
    bandwidth_method: str = "cv",
    adaptive: bool = False,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    verbose: bool = False,
)

Bases: BaseSpatialRegressor

Gaussian geographically weighted regression.

At each target location :math:s_i, the model estimates

.. math::

\hat\beta(s_i) = (X^T W_i X)^{-1}X^T W_i y,

where W_i is produced by a spatial kernel. A fixed bandwidth is a distance; an adaptive bandwidth is an integer neighbour count.

Parameters:

Name Type Description Default
kernel Union[str, Callable[[ndarray, float], ndarray]]

Kernel name or callable accepting (distances, bandwidth).

'gaussian'
bandwidth Union[float, int, str, None]

Numeric bandwidth or automatic-selection criterion. If None, bandwidth_method is used.

'cv'
bandwidth_method str

Criterion used only when bandwidth=None.

'cv'
adaptive bool

Interpret the fitted bandwidth as an integer neighbour count.

False
bandwidth_range Optional[Tuple[float, float]]

User-specified search interval. Adaptive bounds must be integers.

None
optimization_method str

One-dimensional search method used by automatic bandwidth selection.

'golden_section'
fit_intercept bool

Include a local intercept.

True
distance_metric str

Metric forwarded to the core distance implementation.

'euclidean'
sigma2_v1 bool

Residual-variance convention. True uses RSS / (n - trace(S)); False uses RSS / (n - 2 trace(S) + trace(S'S)).

True
verbose bool

Print fit progress.

False
Source code in src/pygwrx/models/gwr.py
def __init__(
    self,
    kernel: Union[str, Callable[[np.ndarray, float], np.ndarray]] = "gaussian",
    bandwidth: Union[float, int, str, None] = "cv",
    bandwidth_method: str = "cv",
    adaptive: bool = False,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    verbose: bool = False,
) -> None:
    if not isinstance(sigma2_v1, (bool, np.bool_)):
        raise TypeError("sigma2_v1 must be boolean.")
    if isinstance(bandwidth, str) and bandwidth.strip().lower() == "adaptive":
        raise ValueError(
            "GWR uses adaptive=True to request a nearest-neighbour bandwidth; "
            "bandwidth must be numeric, None, or one of 'cv', 'aic', 'aicc', 'bic'."
        )
    super().__init__(
        kernel=kernel,
        bandwidth=bandwidth,
        bandwidth_method=bandwidth_method,
        adaptive=adaptive,
        bandwidth_range=bandwidth_range,
        optimization_method=optimization_method,
        fit_intercept=fit_intercept,
        distance_metric=distance_metric,
        verbose=verbose,
    )
    self.sigma2_v1 = bool(sigma2_v1)
    self.S_matrix_: Optional[np.ndarray] = None
    self._reset_inference_state()

fit

fit(
    X: Union[ndarray, DataFrame],
    y: Union[ndarray, Series],
    coords: Union[ndarray, DataFrame],
    *,
    compute_hat_matrix: bool = True,
    compute_local_r2: bool = True,
    compute_inference: bool = True,
    compute_hat_matrix_flag: Optional[bool] = None,
    verbose: Optional[bool] = None
) -> "GWR"

Fit the Gaussian GWR model and return self.

The smoother traces and influence values are always computed. Setting compute_hat_matrix=False avoids storing the full n x n matrix while retaining valid AIC/AICc/BIC, effective-parameter, residual-variance, and influence diagnostics.

compute_hat_matrix_flag is retained as a compatibility alias for older PyGWRx code. New code should use compute_hat_matrix.

Source code in src/pygwrx/models/gwr.py
def fit(
    self,
    X: Union[np.ndarray, pd.DataFrame],
    y: Union[np.ndarray, pd.Series],
    coords: Union[np.ndarray, pd.DataFrame],
    *,
    compute_hat_matrix: bool = True,
    compute_local_r2: bool = True,
    compute_inference: bool = True,
    compute_hat_matrix_flag: Optional[bool] = None,
    verbose: Optional[bool] = None,
) -> "GWR":
    """Fit the Gaussian GWR model and return ``self``.

    The smoother traces and influence values are always computed. Setting
    ``compute_hat_matrix=False`` avoids storing the full ``n x n`` matrix while
    retaining valid AIC/AICc/BIC, effective-parameter, residual-variance, and
    influence diagnostics.

    ``compute_hat_matrix_flag`` is retained as a compatibility alias for older
    PyGWRx code. New code should use ``compute_hat_matrix``.
    """
    if compute_hat_matrix_flag is not None:
        if not isinstance(compute_hat_matrix_flag, (bool, np.bool_)):
            raise TypeError("compute_hat_matrix_flag must be boolean or None.")
        compute_hat_matrix = bool(compute_hat_matrix_flag)
    for name, value in (
        ("compute_hat_matrix", compute_hat_matrix),
        ("compute_local_r2", compute_local_r2),
        ("compute_inference", compute_inference),
    ):
        if not isinstance(value, (bool, np.bool_)):
            raise TypeError(f"{name} must be boolean.")
    if verbose is not None:
        if not isinstance(verbose, (bool, np.bool_)):
            raise TypeError("verbose must be boolean or None.")
        self.verbose = bool(verbose)

    self._validate_gwr_parameters(
        kernel=self.kernel,
        bandwidth=self.bandwidth,
        bandwidth_method=self.bandwidth_method,
        adaptive=self.adaptive,
        bandwidth_range=self.bandwidth_range,
        optimization_method=self.optimization_method,
    )
    if not isinstance(self.sigma2_v1, (bool, np.bool_)):
        raise TypeError("sigma2_v1 must be boolean.")
    self._reset_fit_state()

    try:
        X_arr, y_arr, coords_arr = self._validate_inputs(X, y, coords)
        feature_names = (
            None
            if self.feature_names_in_ is None
            else self.feature_names_in_.copy()
        )
        self._store_training_data(X_arr, y_arr, coords_arr, copy=True)
        self.feature_names_in_ = feature_names

        X_design = (
            add_intercept(self.X_train_) if self.fit_intercept else self.X_train_
        )
        self.kernel_func_ = get_kernel_function(self.kernel)
        self.bandwidth_ = self._resolve_bandwidth(
            X_design, self.y_train_, self.coords_train_
        )

        if self.verbose:
            kind = "adaptive k" if self.adaptive else "fixed distance"
            print(f"Fitting GWR with {kind} bandwidth={self.bandwidth_}...")

        self.inference_enabled_ = bool(compute_inference)
        local_fit = self._fit_training_locations(
            X_design,
            store_hat_matrix=bool(compute_hat_matrix),
            compute_inference=self.inference_enabled_,
        )
        if self.fit_intercept:
            self.intercept_ = local_fit.params[:, 0].copy()
            self.coef_ = local_fit.params[:, 1:].copy()
        else:
            self.intercept_ = np.zeros(self.n_samples_, dtype=float)
            self.coef_ = local_fit.params.copy()

        self.fitted_values_ = local_fit.fitted_values.copy()
        self.residuals_ = self.y_train_ - self.fitted_values_
        self.influence_ = local_fit.influence.copy()
        self.hat_matrix_ = local_fit.hat_matrix
        self.S_matrix_ = self.hat_matrix_  # compatibility alias
        self.diagnostics_ = compute_diagnostics(
            self.y_train_,
            self.fitted_values_,
            compute_gwr_stats=True,
            trace_S=local_fit.trace_S,
            trace_StS=local_fit.trace_StS,
        )

        self.local_r2_ = (
            self._compute_local_r2_from_distances(local_fit.distances)
            if compute_local_r2
            else None
        )
        self._set_inference_results(
            local_fit.covariance_factors,
            trace_S=local_fit.trace_S,
            trace_StS=local_fit.trace_StS,
        )

        self._mark_fitted()
        return self
    except Exception:
        self._reset_fit_state()
        raise

get_local_parameters

get_local_parameters(
    coords: Union[ndarray, DataFrame],
) -> Dict[str, np.ndarray]

Return local intercepts and slopes at arbitrary coordinates.

Source code in src/pygwrx/models/gwr.py
def get_local_parameters(
    self, coords: Union[np.ndarray, pd.DataFrame]
) -> Dict[str, np.ndarray]:
    """Return local intercepts and slopes at arbitrary coordinates."""
    params = self._prediction_parameters(coords)
    return {
        "intercept": np.asarray(params["intercept"], dtype=float).copy(),
        "coef": np.asarray(params["coef"], dtype=float).copy(),
        "coords": np.asarray(params["coords"], dtype=float).copy(),
    }

get_local_coefficients

get_local_coefficients(
    coords: Union[ndarray, DataFrame],
) -> np.ndarray

Compatibility helper returning slopes only.

Source code in src/pygwrx/models/gwr.py
def get_local_coefficients(
    self, coords: Union[np.ndarray, pd.DataFrame]
) -> np.ndarray:
    """Compatibility helper returning slopes only."""
    return self.get_local_parameters(coords)["coef"]

summary

summary() -> str

Return a stable text summary of global and local model results.

Source code in src/pygwrx/models/gwr.py
def summary(self) -> str:
    """Return a stable text summary of global and local model results."""
    self._check_is_fitted()
    if self.X_train_ is None or self.y_train_ is None:
        raise RuntimeError("Training data are unavailable.")

    X_global = add_intercept(self.X_train_) if self.fit_intercept else self.X_train_
    global_beta = np.linalg.lstsq(X_global, self.y_train_, rcond=None)[0]
    global_fitted = X_global @ global_beta
    global_residuals = self.y_train_ - global_fitted
    global_rss = float(np.dot(global_residuals, global_residuals))
    n, p = X_global.shape
    global_df = max(n - p, 1)
    global_sigma2 = global_rss / global_df
    covariance = global_sigma2 * np.linalg.pinv(X_global.T @ X_global)
    global_se = np.sqrt(np.maximum(np.diag(covariance), 0.0))

    feature_names = (
        [str(name) for name in self.feature_names_in_]
        if self.feature_names_in_ is not None
        else [f"x{index}" for index in range(self.X_train_.shape[1])]
    )
    global_names = (["intercept"] if self.fit_intercept else []) + feature_names
    local_matrix = (
        np.column_stack([self.intercept_, self.coef_])
        if self.fit_intercept
        else np.asarray(self.coef_)
    )

    lines = [
        "=" * 78,
        "Gaussian Geographically Weighted Regression (GWR)",
        "=" * 78,
        f"Samples: {n}",
        f"Predictors: {self.X_train_.shape[1]}",
        f"Kernel: {self.kernel}",
        f"Bandwidth: {self.bandwidth_} ({'adaptive neighbours' if self.adaptive else 'fixed distance'})",
        f"Distance metric: {self.distance_metric}",
        f"Residual variance (sigma^2): {self.sigma2_:.6f}",
        "",
        "Global OLS reference",
        "-" * 78,
        f"{'Variable':<22}{'Estimate':>14}{'Std. Error':>16}",
    ]
    for name, estimate, standard_error in zip(global_names, global_beta, global_se):
        lines.append(f"{name:<22}{estimate:>14.6f}{standard_error:>16.6f}")

    lines.extend(
        [
            "",
            "Local coefficient distribution",
            "-" * 78,
            f"{'Variable':<22}{'Min':>12}{'Median':>12}{'Mean':>12}{'Max':>12}",
        ]
    )
    for index, name in enumerate(global_names):
        values = local_matrix[:, index]
        lines.append(
            f"{name:<22}{np.min(values):>12.6f}{np.median(values):>12.6f}"
            f"{np.mean(values):>12.6f}{np.max(values):>12.6f}"
        )

    lines.extend(["", "GWR diagnostics", "-" * 78])
    ordered = (
        ("R-squared", "r2"),
        ("Adjusted R-squared", "adj_r2"),
        ("RSS", "rss"),
        ("RMSE", "rmse"),
        ("MAE", "mae"),
        ("AIC", "aic"),
        ("AICc", "aicc"),
        ("BIC", "bic"),
        ("trace(S) / ENP v1", "trace_S"),
        ("trace(S'S)", "trace_StS"),
        ("ENP v2", "enp_v2"),
        ("EDF v2", "edf_v2"),
    )
    for label, key in ordered:
        value = self.diagnostics_.get(key, np.nan) if self.diagnostics_ else np.nan
        lines.append(f"{label:<30}{value:>14.6f}")
    lines.append("=" * 78)
    return "\n".join(lines)

GWRPredictionResult

Rich prediction result returned by :meth:GWR.predict_result.

Property Value
Type class
Import from pygwrx.models import GWRPredictionResult
Signature GWRPredictionResult(predictions: 'np.ndarray', coef: 'np.ndarray', intercept: 'np.ndarray', coords: 'np.ndarray', feature_names: 'Tuple[str, ...]', coef_standard_errors: 'Optional[np.ndarray]' = None, intercept_standard_errors: 'Optional[np.ndarray]' = None, coef_t_values: 'Optional[np.ndarray]' = None, intercept_t_values: 'Optional[np.ndarray]' = None) -> None
Maintained example examples/models/01_gwr.py

GWRPredictionResult dataclass

GWRPredictionResult(
    predictions: ndarray,
    coef: ndarray,
    intercept: ndarray,
    coords: ndarray,
    feature_names: Tuple[str, ...],
    coef_standard_errors: Optional[ndarray] = None,
    intercept_standard_errors: Optional[ndarray] = None,
    coef_t_values: Optional[ndarray] = None,
    intercept_t_values: Optional[ndarray] = None,
)

Rich prediction result returned by :meth:GWR.predict_result.

Runnable examples used on this page

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

"""Load a bundled real dataset, fit GWR, inspect it, and predict."""

from __future__ import annotations

# 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 pygwrx import GWR, GWRPredictionResult
from pygwrx.io import load_columbus

bundle = load_columbus(return_type="dict")
X = bundle["data"]
y = bundle["target"]
coords = bundle["coords"]

print("dataset=", bundle["description"])
print("features=", bundle["feature_names"])
print("license=", bundle["license"])

model = GWR(kernel="bisquare", bandwidth=24, adaptive=True).fit(X, y, coords)
print(model.summary())
print("score=", model.score(X, y, coords))

result = model.predict_result(X[:3], coords[:3])
assert isinstance(result, GWRPredictionResult)
print(result.to_frame())