Skip to content

GWGLM

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

GWGLM

Geographically weighted generalized linear model.

Property Value
Type class
Import from pygwrx.models import GWGLM
Signature GWGLM(family: 'FamilyName' = 'gaussian', kernel: 'KernelLike' = 'bisquare', bandwidth: 'BandwidthLike' = 'cv', bandwidth_method: 'str' = 'aicc', adaptive: 'bool' = False, bandwidth_range: 'Optional[Tuple[float, float]]' = None, optimization_method: 'str' = 'golden_section', max_iter: 'int' = 100, tol: 'float' = 1e-06, fit_intercept: 'bool' = True, distance_metric: 'str' = 'euclidean', sigma2_v1: 'bool' = True, verbose: 'bool' = False) -> 'None'
Maintained example examples/models/06_gwglm.py

GWGLM

GWGLM(
    family: FamilyName = "gaussian",
    kernel: KernelLike = "bisquare",
    bandwidth: BandwidthLike = "cv",
    bandwidth_method: str = "aicc",
    adaptive: bool = False,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    max_iter: int = 100,
    tol: float = 1e-06,
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    verbose: bool = False,
)

Bases: GWR

Geographically weighted generalized linear model.

The estimator supports three canonical families:

  • "gaussian" with an identity link;
  • "poisson" with a log link and optional exposure or log-offset;
  • "binomial" for Bernoulli responses with a logit link.

Poisson and Binomial models are fitted independently at each regression location by local iteratively weighted least squares (IWLS). Spatial kernel weights and GLM working weights are multiplied inside each local fit.

Parameters:

Name Type Description Default
family FamilyName

Response distribution. Supported values are "gaussian", "poisson", and "binomial".

'gaussian'
kernel KernelLike

Spatial kernel name or callable.

'bisquare'
bandwidth BandwidthLike

Numeric bandwidth or automatic-selection criterion. A fixed bandwidth is a distance; an adaptive bandwidth is a neighbour count.

'cv'
bandwidth_method str

Selection criterion used when bandwidth=None.

'aicc'
adaptive bool

Whether the bandwidth represents nearest-neighbour count.

False
bandwidth_range Optional[Tuple[float, float]]

Optional lower and upper search bounds.

None
optimization_method str

"golden_section", "brent", or "grid".

'golden_section'
max_iter int

Maximum IWLS iterations per local model.

100
tol float

IWLS convergence tolerance.

1e-06
fit_intercept bool

Whether to include a local intercept.

True
distance_metric str

Distance metric used by the spatial kernel.

'euclidean'
sigma2_v1 bool

Gaussian residual-variance convention inherited from GWR.

True
verbose bool

Whether to print fitting and bandwidth-search progress.

False

Attributes:

Name Type Description
family_

Normalized fitted family name.

bandwidth_

Selected or user-specified bandwidth.

coef_

Local slope coefficients.

intercept_

Local intercepts.

fitted_values_

Fitted conditional means.

linear_predictor_

Fitted values on the link scale.

residuals_

Response residuals y - fitted_values_.

deviance_residuals_

Signed deviance residuals.

pearson_residuals_

Pearson residuals.

parameter_standard_errors_

Local parameter standard errors.

parameter_z_values_

Local Wald z statistics for non-Gaussian models.

iteration_counts_

Number of IWLS iterations at each location.

converged_

Whether every local IWLS fit converged.

exposure_train_

Poisson exposure used during fitting.

Notes

offset in :meth:fit and :meth:predict is an additive offset on the linear-predictor scale. For Poisson models it is equivalent to log(exposure). Supply at most one of exposure and offset.

Grouped-binomial responses are intentionally not accepted. The current Binomial implementation is Bernoulli only and requires values in {0, 1}.

References

Nakaya, T., Fotheringham, A. S., Brunsdon, C., and Charlton, M. (2005). Geographically weighted Poisson regression for disease association mapping. Statistics in Medicine, 24, 2695-2717.

Oshan, T. M., Li, Z., Kang, W., Wolf, L. J., and Fotheringham, A. S. (2019). mgwr: A Python implementation of multiscale geographically weighted regression for investigating process spatial heterogeneity and scale. ISPRS International Journal of Geo-Information, 8, 269.

Source code in src/pygwrx/models/glm_gwr.py
def __init__(
    self,
    family: FamilyName = "gaussian",
    kernel: KernelLike = "bisquare",
    bandwidth: BandwidthLike = "cv",
    bandwidth_method: str = "aicc",
    adaptive: bool = False,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    optimization_method: str = "golden_section",
    max_iter: int = 100,
    tol: float = 1.0e-6,
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    verbose: bool = False,
) -> None:
    normalized_family = self._normalize_family(family)
    if isinstance(max_iter, (bool, np.bool_)) or not isinstance(
        max_iter, (int, np.integer)
    ):
        raise TypeError("max_iter must be a positive integer.")
    if int(max_iter) <= 0:
        raise ValueError("max_iter must be greater than zero.")
    if isinstance(tol, (bool, np.bool_)):
        raise TypeError("tol must be a positive real scalar.")
    tol_value = float(tol)
    if not np.isfinite(tol_value) or tol_value <= 0.0:
        raise ValueError("tol must be finite and greater than zero.")

    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,
        sigma2_v1=sigma2_v1,
        verbose=verbose,
    )
    self.family = normalized_family
    self.max_iter = int(max_iter)
    self.tol = tol_value
    self._reset_glm_state()

fit

fit(
    X: ArrayLike,
    y: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None,
    compute_hat_matrix: bool = False,
    compute_inference: bool = True,
    compute_local_r2: bool = True
) -> "GWGLM"

Fit the geographically weighted generalized linear model.

Parameters:

Name Type Description Default
X ArrayLike

Predictor matrix with shape (n_samples, n_features).

required
y ArrayLike

Response vector. Poisson values must be non-negative; Binomial values must be Bernoulli outcomes in {0, 1}.

required
coords ArrayLike

Spatial coordinates with shape (n_samples, 2).

required
exposure Optional[object]

Positive Poisson exposure. Scalar values are broadcast.

None
offset Optional[object]

Poisson log-exposure offset. Supply at most one of exposure and offset.

None
compute_hat_matrix bool

Whether to retain the full local smoother matrix.

False
compute_inference bool

Whether to compute local standard errors and Wald statistics. Non-Gaussian diagnostics still retain trace statistics.

True
compute_local_r2 bool

Gaussian-only option forwarded to standard GWR.

True

Returns:

Type Description
'GWGLM'

The fitted estimator.

Source code in src/pygwrx/models/glm_gwr.py
def fit(
    self,
    X: ArrayLike,
    y: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None,
    compute_hat_matrix: bool = False,
    compute_inference: bool = True,
    compute_local_r2: bool = True,
) -> "GWGLM":
    """Fit the geographically weighted generalized linear model.

    Args:
        X: Predictor matrix with shape ``(n_samples, n_features)``.
        y: Response vector. Poisson values must be non-negative; Binomial
            values must be Bernoulli outcomes in ``{0, 1}``.
        coords: Spatial coordinates with shape ``(n_samples, 2)``.
        exposure: Positive Poisson exposure. Scalar values are broadcast.
        offset: Poisson log-exposure offset. Supply at most one of exposure
            and offset.
        compute_hat_matrix: Whether to retain the full local smoother matrix.
        compute_inference: Whether to compute local standard errors and Wald
            statistics. Non-Gaussian diagnostics still retain trace statistics.
        compute_local_r2: Gaussian-only option forwarded to standard GWR.

    Returns:
        The fitted estimator.
    """
    self._reset_fit_state()
    self.family = self._normalize_family(self.family)
    if self.family == "gaussian":
        if exposure is not None or offset is not None:
            raise ValueError("Gaussian GWGLM does not use exposure or offset.")
        super().fit(
            X,
            y,
            coords,
            compute_hat_matrix=compute_hat_matrix,
            compute_inference=compute_inference,
            compute_local_r2=compute_local_r2,
        )
        self.family_ = "gaussian"
        self.mu_ = self.fitted_values_.copy()
        self.linear_predictor_ = self.fitted_values_.copy()
        self.exposure_train_ = np.ones(self.n_samples_, dtype=float)
        self.offset_train_ = np.zeros(self.n_samples_, dtype=float)
        self.deviance_residuals_ = self.residuals_.copy()
        sigma = np.sqrt(max(float(self.sigma2_ or 0.0), _EPS))
        self.pearson_residuals_ = self.residuals_ / sigma
        self.deviance_ = float(np.dot(self.residuals_, self.residuals_))
        null_residuals = self.y_train_ - np.mean(self.y_train_)
        self.null_deviance_ = float(np.dot(null_residuals, null_residuals))
        self.percent_deviance_ = (
            1.0 - self.deviance_ / self.null_deviance_
            if self.null_deviance_ > _EPS
            else np.nan
        )
        self.adjusted_percent_deviance_ = float(
            self.diagnostics_.get("adjusted_r2", np.nan)
        )
        self.log_likelihood_ = self._log_likelihood(self.y_train_, self.mu_)
        self.iteration_counts_ = np.ones(self.n_samples_, dtype=int)
        self.local_converged_ = np.ones(self.n_samples_, dtype=bool)
        self.converged_ = True
        self.final_working_weights_ = np.ones(self.n_samples_, dtype=float)
        self.parameter_z_values_ = (
            None
            if self.parameter_t_values_ is None
            else self.parameter_t_values_.copy()
        )
        self.intercept_z_ = (
            None if self.intercept_t_ is None else self.intercept_t_.copy()
        )
        self.coef_z_ = None if self.coef_t_ is None else self.coef_t_.copy()
        return self

    try:
        X_arr, y_arr, coords_arr = self._validate_inputs(X, y, coords)
        self._validate_response(y_arr)
        exposure_arr, offset_arr = self._prepare_exposure(
            y_arr.size, exposure=exposure, offset=offset
        )
        self._store_training_data(X_arr, y_arr, coords_arr, copy=True)
        self.exposure_train_ = exposure_arr.copy()
        self.offset_train_ = offset_arr.copy()
        X_design = add_intercept(X_arr) if self.fit_intercept else X_arr
        self.kernel_func_ = get_kernel_function(self.kernel)
        self.bandwidth_ = self._resolve_non_gaussian_bandwidth(
            X_design, y_arr, coords_arr, exposure_arr
        )
        if self.verbose:
            kind = "adaptive neighbours" if self.adaptive else "fixed distance"
            print(
                f"Fitting {self.family.title()} GWGLM with {kind} "
                f"bandwidth={self.bandwidth_}..."
            )
        fit_result = self._fit_non_gaussian_locations(
            X_design,
            y_arr,
            coords_arr,
            exposure_arr,
            self.bandwidth_,
            store_hat_matrix=bool(compute_hat_matrix),
        )
        if self.fit_intercept:
            self.intercept_ = fit_result.params[:, 0].copy()
            self.coef_ = fit_result.params[:, 1:].copy()
        else:
            self.intercept_ = np.zeros(y_arr.size, dtype=float)
            self.coef_ = fit_result.params.copy()
        self.family_ = self.family
        self.fitted_values_ = fit_result.fitted_values.copy()
        self.mu_ = self.fitted_values_.copy()
        self.linear_predictor_ = fit_result.linear_predictor.copy()
        self.residuals_ = y_arr - self.fitted_values_
        self.hat_matrix_ = fit_result.hat_matrix
        self.S_matrix_ = self.hat_matrix_
        self.iteration_counts_ = fit_result.iteration_counts.copy()
        self.local_converged_ = fit_result.converged.copy()
        self.converged_ = bool(np.all(self.local_converged_))
        self.final_working_weights_ = fit_result.final_working_weights.copy()
        self.deviance_residuals_ = self._deviance_residuals(y_arr, self.mu_)
        if self.family == "poisson":
            variance = np.clip(self.mu_, _EPS, None)
        else:
            variance = np.clip(self.mu_ * (1.0 - self.mu_), _EPS, None)
        self.pearson_residuals_ = self.residuals_ / np.sqrt(variance)
        self.diagnostics_ = self._information_diagnostics(
            y_arr,
            self.mu_,
            exposure_arr,
            trace_s=fit_result.trace_S,
            trace_sts=fit_result.trace_StS,
        )
        self.deviance_ = self.diagnostics_["deviance"]
        self.null_deviance_ = self.diagnostics_["null_deviance"]
        self.log_likelihood_ = self.diagnostics_["log_likelihood"]
        self.percent_deviance_ = self.diagnostics_["percent_deviance"]
        self.adjusted_percent_deviance_ = self.diagnostics_[
            "adjusted_percent_deviance"
        ]
        if compute_inference:
            self._set_non_gaussian_inference(fit_result, self.diagnostics_)
        else:
            self.influence_ = fit_result.influence.copy()
            self.inference_enabled_ = False
        self.local_r2_ = None
        if isinstance(self.bandwidth, str) or self.bandwidth is None:
            method = (
                self.bandwidth.strip().lower()
                if isinstance(self.bandwidth, str)
                else self.bandwidth_method.strip().lower()
            )
            if method == "cv":
                cv_score, cv_residuals = self._leave_one_out_score(
                    X_design,
                    y_arr,
                    coords_arr,
                    exposure_arr,
                    self.bandwidth_,
                    return_residuals=True,
                )
                self.bandwidth_selection_score_ = float(cv_score)
                self.cv_residuals_ = cv_residuals.copy()
                self.cv_contributions_ = cv_residuals**2
        self._mark_fitted()
        return self
    except Exception:
        self._reset_fit_state()
        raise

predict

predict(
    X: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None
) -> np.ndarray

Predict conditional means at target locations.

Source code in src/pygwrx/models/glm_gwr.py
def predict(
    self,
    X: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None,
) -> np.ndarray:
    """Predict conditional means at target locations."""
    return self.predict_result(
        X, coords, exposure=exposure, offset=offset
    ).predictions

predict_result

predict_result(
    X: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None
) -> Union[GWGLMPredictionResult, GWRPredictionResult]

Return predictions, local parameters, and optional inference results.

Source code in src/pygwrx/models/glm_gwr.py
def predict_result(
    self,
    X: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None,
) -> Union[GWGLMPredictionResult, GWRPredictionResult]:
    """Return predictions, local parameters, and optional inference results."""
    if self.family_ == "gaussian":
        if exposure is not None or offset is not None:
            raise ValueError("Gaussian GWGLM does not use exposure or offset.")
        return super().predict_result(X, coords)
    X_arr, coords_arr = self._validate_prediction_inputs(X, coords)
    exposure_arr, offset_arr = self._prepare_exposure(
        X_arr.shape[0], exposure=exposure, offset=offset
    )
    params = self._prediction_non_gaussian_parameters(coords_arr)
    coef = params["coef"]
    intercept = params["intercept"]
    linear_predictor = np.einsum("ij,ij->i", X_arr, coef) + intercept
    if self.family_ == "poisson":
        predictions = np.exp(np.clip(linear_predictor + offset_arr, -700.0, 700.0))
        result_exposure: Optional[np.ndarray] = exposure_arr
    else:
        predictions = expit(linear_predictor)
        result_exposure = None
    standard_errors = params["standard_errors"]
    z_values = params["z_values"]
    if self.fit_intercept:
        intercept_se = standard_errors[:, 0]
        coef_se = standard_errors[:, 1:]
        intercept_z = z_values[:, 0]
        coef_z = z_values[:, 1:]
    else:
        intercept_se = np.zeros(X_arr.shape[0], dtype=float)
        coef_se = standard_errors
        intercept_z = np.full(X_arr.shape[0], np.nan, dtype=float)
        coef_z = z_values
    names = (
        tuple(str(name) for name in self.feature_names_in_)
        if self.feature_names_in_ is not None
        else tuple(f"x{index}" for index in range(X_arr.shape[1]))
    )
    return GWGLMPredictionResult(
        predictions=np.asarray(predictions, dtype=float),
        linear_predictor=np.asarray(linear_predictor, dtype=float),
        coef=np.asarray(coef, dtype=float),
        intercept=np.asarray(intercept, dtype=float),
        coords=np.asarray(coords_arr, dtype=float),
        feature_names=names,
        family=str(self.family_),
        exposure=result_exposure,
        coef_standard_errors=coef_se,
        intercept_standard_errors=intercept_se,
        coef_z_values=coef_z,
        intercept_z_values=intercept_z,
    )

score

score(
    X: ArrayLike,
    y: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None
) -> float

Return R² for Gaussian models or deviance explained otherwise.

Source code in src/pygwrx/models/glm_gwr.py
def score(
    self,
    X: ArrayLike,
    y: ArrayLike,
    coords: ArrayLike,
    *,
    exposure: Optional[object] = None,
    offset: Optional[object] = None,
) -> float:
    """Return R² for Gaussian models or deviance explained otherwise."""
    y_arr = np.asarray(y, dtype=float).reshape(-1)
    predictions = self.predict(X, coords, exposure=exposure, offset=offset)
    if self.family_ == "gaussian":
        residual = y_arr - predictions
        total = y_arr - np.mean(y_arr)
        denominator = float(np.dot(total, total))
        return 1.0 - float(np.dot(residual, residual)) / denominator
    exposure_arr, _ = self._prepare_exposure(
        y_arr.size, exposure=exposure, offset=offset
    )
    deviance = self._deviance(y_arr, predictions)
    null_deviance = self._deviance(y_arr, self._null_mean(y_arr, exposure_arr))
    return 1.0 - deviance / null_deviance

to_frame

to_frame() -> pd.DataFrame

Return training-location parameters and GLM diagnostics.

Source code in src/pygwrx/models/glm_gwr.py
def to_frame(self) -> pd.DataFrame:
    """Return training-location parameters and GLM diagnostics."""
    frame = super().to_frame()
    for name, values in (
        ("linear_predictor", self.linear_predictor_),
        ("deviance_residual", self.deviance_residuals_),
        ("pearson_residual", self.pearson_residuals_),
        ("influence", self.influence_),
        ("iteration_count", self.iteration_counts_),
        ("local_converged", self.local_converged_),
    ):
        if values is not None:
            frame[name] = np.asarray(values).reshape(-1)
    if self.family_ == "poisson" and self.exposure_train_ is not None:
        frame["exposure"] = self.exposure_train_
        frame["offset"] = self.offset_train_
    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.n_features_in_ or 0)]
    )
    if self.intercept_se_ is not None:
        frame["intercept_se"] = self.intercept_se_
    if self.intercept_z_ is not None:
        frame["intercept_z"] = self.intercept_z_
    if self.coef_se_ is not None:
        for index, name in enumerate(feature_names):
            frame[f"se_{name}"] = self.coef_se_[:, index]
    if self.coef_z_ is not None:
        for index, name in enumerate(feature_names):
            frame[f"z_{name}"] = self.coef_z_[:, index]
    return frame

summary

summary() -> str

Return a stable text summary of GWGLM results.

Source code in src/pygwrx/models/glm_gwr.py
def summary(self) -> str:
    """Return a stable text summary of GWGLM results."""
    self._check_is_fitted()
    if self.family_ == "gaussian":
        return (
            super()
            .summary()
            .replace(
                "Gaussian Geographically Weighted Regression (GWR)",
                "Gaussian Geographically Weighted Generalized Linear Model (GWGLM)",
                1,
            )
        )
    if self.diagnostics_ is None:
        raise RuntimeError("GWGLM diagnostics are unavailable.")
    lines = [
        "=" * 78,
        f"{self.family_.title()} Geographically Weighted GLM (GWGLM)",
        "=" * 78,
        f"Samples: {self.n_samples_}",
        f"Predictors: {self.n_features_in_}",
        f"Kernel: {self.kernel}",
        f"Bandwidth: {self.bandwidth_} ({'adaptive neighbours' if self.adaptive else 'fixed distance'})",
        f"Distance metric: {self.distance_metric}",
        f"All local fits converged: {self.converged_}",
        f"Maximum local IWLS iterations: {int(np.max(self.iteration_counts_))}",
        "",
        "Model diagnostics",
        "-" * 78,
        f"Deviance: {self.deviance_:.6f}",
        f"Null deviance: {self.null_deviance_:.6f}",
        f"Deviance explained: {self.percent_deviance_:.6f}",
        f"Adjusted deviance explained: {self.adjusted_percent_deviance_:.6f}",
        f"Effective parameters (trace(S)): {self.diagnostics_['effective_params']:.6f}",
        f"AIC: {self.diagnostics_['aic']:.6f}",
        f"AICc: {self.diagnostics_['aicc']:.6f}",
        f"BIC: {self.diagnostics_['bic']:.6f}",
        "=" * 78,
    ]
    return "\n".join(lines)

GWGLMPredictionResult

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

Property Value
Type class
Import from pygwrx.models import GWGLMPredictionResult
Signature GWGLMPredictionResult(predictions: 'np.ndarray', linear_predictor: 'np.ndarray', coef: 'np.ndarray', intercept: 'np.ndarray', coords: 'np.ndarray', feature_names: 'Tuple[str, ...]', family: 'str', exposure: 'Optional[np.ndarray]' = None, coef_standard_errors: 'Optional[np.ndarray]' = None, intercept_standard_errors: 'Optional[np.ndarray]' = None, coef_z_values: 'Optional[np.ndarray]' = None, intercept_z_values: 'Optional[np.ndarray]' = None) -> None
Maintained example examples/models/06_gwglm.py

GWGLMPredictionResult dataclass

GWGLMPredictionResult(
    predictions: ndarray,
    linear_predictor: ndarray,
    coef: ndarray,
    intercept: ndarray,
    coords: ndarray,
    feature_names: Tuple[str, ...],
    family: str,
    exposure: Optional[ndarray] = None,
    coef_standard_errors: Optional[ndarray] = None,
    intercept_standard_errors: Optional[ndarray] = None,
    coef_z_values: Optional[ndarray] = None,
    intercept_z_values: Optional[ndarray] = None,
)

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

to_frame

to_frame() -> pd.DataFrame

Return prediction results as a pandas DataFrame.

Source code in src/pygwrx/models/glm_gwr.py
def to_frame(self) -> pd.DataFrame:
    """Return prediction results as a pandas DataFrame."""
    data: Dict[str, np.ndarray] = {
        "coord_0": self.coords[:, 0],
        "coord_1": self.coords[:, 1],
        "prediction": self.predictions,
        "linear_predictor": self.linear_predictor,
        "intercept": self.intercept,
    }
    if self.exposure is not None:
        data["exposure"] = self.exposure
    if self.intercept_standard_errors is not None:
        data["intercept_se"] = self.intercept_standard_errors
    if self.intercept_z_values is not None:
        data["intercept_z"] = self.intercept_z_values
    for index, name in enumerate(self.feature_names):
        data[f"coef_{name}"] = self.coef[:, index]
        if self.coef_standard_errors is not None:
            data[f"se_{name}"] = self.coef_standard_errors[:, index]
        if self.coef_z_values is not None:
            data[f"z_{name}"] = self.coef_z_values[:, index]
    return pd.DataFrame(data)

to_geodataframe

to_geodataframe(crs: Optional[Union[str, int]] = None)

Return prediction results as a point GeoDataFrame.

Source code in src/pygwrx/models/glm_gwr.py
def to_geodataframe(self, crs: Optional[Union[str, int]] = None):
    """Return prediction results as a point GeoDataFrame."""
    from pygwrx.io import to_geodataframe

    frame = self.to_frame()
    columns = [column for column in frame if not column.startswith("coord_")]
    return to_geodataframe(
        frame[columns].to_numpy(dtype=float),
        None,
        self.coords,
        feature_names=columns,
        crs=crs,
    )

Runnable examples used on this page

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

"""Fit Gaussian, binomial, and Poisson GWGLM families."""

# 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))

import numpy as np
from _common import count_regression, print_model_result, spatial_regression

from pygwrx import GWGLM, GWGLMPredictionResult

X, y, coords = spatial_regression(p=2)
gaussian = GWGLM(family="gaussian", bandwidth=24, adaptive=True).fit(X, y, coords)
print_model_result(gaussian)

binary = (y > np.median(y)).astype(int)
binomial = GWGLM(family="binomial", bandwidth=24, adaptive=True).fit(X, binary, coords)
binomial_result = binomial.predict_result(X.iloc[:3], coords.iloc[:3])
assert isinstance(binomial_result, GWGLMPredictionResult)
print(binomial_result.to_frame())

Xc, counts, coordsc, exposure = count_regression()
poisson = GWGLM(family="poisson", bandwidth=24, adaptive=True).fit(
    Xc, counts, coordsc, exposure=exposure
)
print(
    "poisson means=",
    poisson.predict(Xc.iloc[:3], coordsc.iloc[:3], exposure=exposure[:3]),
)