Skip to content

SGWR

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

SGWR

Similarity and geographically weighted regression.

Property Value
Type class
Import from pygwrx.models import SGWR
Signature SGWR(bandwidth: 'Bandwidth' = 'aicc', adaptive: 'bool' = True, kernel: 'str' = 'bisquare', alpha: 'Alpha' = 'aicc', similarity_vars: 'Optional[Sequence[Union[int, str]]]' = None, *, standardize_similarity: 'bool' = True, bandwidth_kernel: 'Optional[str]' = None, bandwidth_range: 'Optional[Tuple[float, float]]' = None, alpha_range: 'Tuple[float, float]' = (0.01, 1.0), alpha_grid_size: 'int' = 21, fit_intercept: 'bool' = True, distance_metric: 'str' = 'euclidean', sigma2_v1: 'bool' = True, ridge: 'float' = 0.0, store_weights: 'bool' = True, verbose: 'bool' = False) -> 'None'
Maintained example examples/models/15_sgwr.py

SGWR

SGWR(
    bandwidth: Bandwidth = "aicc",
    adaptive: bool = True,
    kernel: str = "bisquare",
    alpha: Alpha = "aicc",
    similarity_vars: Optional[
        Sequence[Union[int, str]]
    ] = None,
    *,
    standardize_similarity: bool = True,
    bandwidth_kernel: Optional[str] = None,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    alpha_range: Tuple[float, float] = (0.01, 1.0),
    alpha_grid_size: int = 21,
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    ridge: float = 0.0,
    store_weights: bool = True,
    verbose: bool = False
)

Similarity and geographically weighted regression.

For calibration location :math:i, SGWR constructs

.. math::

W_i^{GS} = \alpha W_i^G + (1 - \alpha) W_i^S,

where :math:W_i^G is a geographic kernel and the published similarity kernel is

.. math::

w_{ij}^S = \exp\left[-\left(\frac{1}{m}
\sum_{r=1}^{m}|z_{ir}-z_{jr}|\right)^2\right].

The similarity variables z are standardized using the training-sample mean and population standard deviation before distances are calculated. alpha=1 is ordinary GWR and alpha=0 is similarity-only local regression.

Parameters:

Name Type Description Default
bandwidth Bandwidth

Geographic bandwidth. Numeric values are used directly. None or "aicc" selects the bandwidth from a pure GWR using AICc before optimizing alpha.

'aicc'
adaptive bool

Interpret a numeric bandwidth as a one-based neighbour count.

True
kernel str

Geographic kernel used in the final SGWR fit.

'bisquare'
alpha Alpha

Geographic mixing proportion. Numeric values lie in [0, 1]. None or "aicc" selects alpha by SGWR AICc.

'aicc'
similarity_vars Optional[Sequence[Union[int, str]]]

Predictor names or zero-based column indices used to construct attribute similarity. None uses all predictors.

None
standardize_similarity bool

Standardize similarity variables before the published mean-absolute-distance calculation.

True
bandwidth_kernel Optional[str]

Optional kernel used only for automatic pure-GWR bandwidth selection. This permits the software-paper hybrid of an adaptive bi-square search followed by an adaptive Gaussian SGWR fit.

None
bandwidth_range Optional[Tuple[float, float]]

Optional search bounds passed to the standard GWR bandwidth selector.

None
alpha_range Tuple[float, float]

Bounds used for automatic alpha selection.

(0.01, 1.0)
alpha_grid_size int

Number of deterministic coarse alpha candidates before bounded local refinement.

21
fit_intercept bool

Include a local intercept.

True
distance_metric str

Coordinate distance metric used by pyGWRx.

'euclidean'
sigma2_v1 bool

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

True
ridge float

Optional non-negative numerical ridge added to slope diagonals. The intercept is not penalized.

0.0
store_weights bool

Store the three n x n training weight matrices.

True
verbose bool

Print selection and fit progress.

False
References

Lessani, M. N., & Li, Z. (2024). SGWR: similarity and geographically weighted regression. International Journal of Geographical Information Science, 38(7), 1232-1255.

Lessani, M. N., & Li, Z. (2025). Enhancing the computational efficiency of the SGWR model and introducing its software implementation. Annals of GIS.

Source code in src/pygwrx/models/sgwr.py
def __init__(
    self,
    bandwidth: Bandwidth = "aicc",
    adaptive: bool = True,
    kernel: str = "bisquare",
    alpha: Alpha = "aicc",
    similarity_vars: Optional[Sequence[Union[int, str]]] = None,
    *,
    standardize_similarity: bool = True,
    bandwidth_kernel: Optional[str] = None,
    bandwidth_range: Optional[Tuple[float, float]] = None,
    alpha_range: Tuple[float, float] = (0.01, 1.0),
    alpha_grid_size: int = 21,
    fit_intercept: bool = True,
    distance_metric: str = "euclidean",
    sigma2_v1: bool = True,
    ridge: float = 0.0,
    store_weights: bool = True,
    verbose: bool = False,
) -> None:
    self.bandwidth = bandwidth
    self.adaptive = self._validate_boolean(adaptive, "adaptive")
    self.kernel = self._validate_kernel_name(kernel, "kernel")
    self.alpha = alpha
    self.similarity_vars = similarity_vars
    self.standardize_similarity = self._validate_boolean(
        standardize_similarity, "standardize_similarity"
    )
    self.bandwidth_kernel = (
        self.kernel
        if bandwidth_kernel is None
        else self._validate_kernel_name(bandwidth_kernel, "bandwidth_kernel")
    )
    self.bandwidth_range = bandwidth_range
    self.alpha_range = self._validate_alpha_range(alpha_range)
    self.alpha_grid_size = self._validate_alpha_grid_size(alpha_grid_size)
    self.fit_intercept = self._validate_boolean(fit_intercept, "fit_intercept")
    self.distance_metric = self._validate_distance_metric(distance_metric)
    self.sigma2_v1 = self._validate_boolean(sigma2_v1, "sigma2_v1")
    self.ridge = self._validate_nonnegative_float(ridge, "ridge")
    self.store_weights = self._validate_boolean(store_weights, "store_weights")
    self.verbose = self._validate_boolean(verbose, "verbose")

    self.kernel_func_ = get_kernel_function(self.kernel)
    self._reset_fit_state()

fit

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

Fit Gaussian SGWR at the observed locations.

Source code in src/pygwrx/models/sgwr.py
def fit(
    self,
    X: Union[np.ndarray, pd.DataFrame],
    y: Union[np.ndarray, pd.Series],
    coords: Union[np.ndarray, pd.DataFrame],
) -> "SGWR":
    """Fit Gaussian SGWR at the observed locations."""
    self._reset_fit_state()
    try:
        X_arr, feature_names = self._coerce_X(X)
        y_arr = self._coerce_y(y, X_arr.shape[0])
        coords_arr = validate_coords(coords)
        if coords_arr.shape[0] != X_arr.shape[0]:
            raise ValueError("X and coords must contain the same number of rows.")

        similarity_indices = self._resolve_similarity_indices(
            feature_names, X_arr.shape[1]
        )
        self.similarity_indices_ = similarity_indices
        self.feature_names_ = feature_names
        self.similarity_feature_names_ = tuple(
            feature_names[index] for index in similarity_indices
        )
        X_similarity = self._fit_similarity_scaler(X_arr[:, similarity_indices])
        X_design = add_intercept(X_arr) if self.fit_intercept else X_arr.copy()

        bandwidth = self._select_bandwidth(X, y_arr, coords_arr, X_design)
        spatial = self._spatial_weights(coords_arr, coords_arr, bandwidth)
        similarity = self._similarity_weights(X_similarity, X_similarity)
        alpha = self._select_alpha(X_design, y_arr, spatial, similarity)
        combined = self._combine_weights(spatial, similarity, alpha)

        local_fit = self._fit_weight_matrix(
            X_design,
            y_arr,
            combined,
            X_design,
            compute_covariance=True,
        )
        residuals = y_arr - local_fit.fitted_values
        diagnostics = compute_diagnostics(
            y_arr,
            local_fit.fitted_values,
            hat_matrix=local_fit.hat_matrix,
            compute_gwr_stats=True,
        )
        trace_s = float(diagnostics["trace_S"])
        trace_sts = float(diagnostics["trace_StS"])
        denominator = (
            y_arr.size - trace_s
            if self.sigma2_v1
            else y_arr.size - 2.0 * trace_s + trace_sts
        )
        if denominator <= 0.0:
            raise ValueError(
                "Residual effective degrees of freedom are not positive; "
                "increase the bandwidth or simplify the design."
            )
        sigma2 = float(np.dot(residuals, residuals) / denominator)
        if local_fit.covariance_diagonal is None:
            raise RuntimeError("SGWR covariance factors were not computed.")
        parameter_se = np.sqrt(
            np.maximum(local_fit.covariance_diagonal * sigma2, 0.0)
        )
        parameter_t = np.divide(
            local_fit.parameters,
            parameter_se,
            out=np.full_like(local_fit.parameters, np.nan),
            where=parameter_se > 0.0,
        )
        influence = np.diag(local_fit.hat_matrix)
        standardized_residuals = np.divide(
            residuals,
            np.sqrt(np.maximum(sigma2 * (1.0 - influence), 0.0)),
            out=np.full_like(residuals, np.nan),
            where=(1.0 - influence) > 0.0,
        )
        cooks = np.divide(
            standardized_residuals**2 * influence,
            max(trace_s, np.finfo(float).eps) * (1.0 - influence),
            out=np.full_like(residuals, np.nan),
            where=(1.0 - influence) > 0.0,
        )

        local_r2 = np.empty(y_arr.size, dtype=float)
        for index, weight_row in enumerate(combined):
            weight_sum = float(np.sum(weight_row))
            local_mean = float(np.dot(weight_row, y_arr) / weight_sum)
            tss = float(np.dot(weight_row, (y_arr - local_mean) ** 2))
            rss = float(np.dot(weight_row, residuals**2))
            local_r2[index] = np.nan if tss <= 0.0 else 1.0 - rss / tss

        self.bandwidth_ = bandwidth
        self.alpha_ = alpha
        self.X_train_ = X_arr.copy()
        self.X_design_ = X_design
        self.y_train_ = y_arr
        self.coords_train_ = coords_arr
        self.parameters_ = local_fit.parameters
        if self.fit_intercept:
            self.intercept_ = local_fit.parameters[:, 0]
            self.coef_ = local_fit.parameters[:, 1:]
            self.intercept_se_ = parameter_se[:, 0]
            self.coef_se_ = parameter_se[:, 1:]
            self.intercept_t_ = parameter_t[:, 0]
            self.coef_t_ = parameter_t[:, 1:]
        else:
            self.intercept_ = np.zeros(y_arr.size, dtype=float)
            self.coef_ = local_fit.parameters
            self.intercept_se_ = np.zeros(y_arr.size, dtype=float)
            self.coef_se_ = parameter_se
            self.intercept_t_ = np.full(y_arr.size, np.nan)
            self.coef_t_ = parameter_t
        self.coefficients_ = self.coef_
        self.fitted_values_ = local_fit.fitted_values
        self.residuals_ = residuals
        self.hat_matrix_ = local_fit.hat_matrix
        self.influence_ = influence
        self.parameter_covariance_diagonal_ = local_fit.covariance_diagonal
        self.parameter_standard_errors_ = parameter_se
        self.parameter_t_values_ = parameter_t
        self.sigma2_ = sigma2
        self.standardized_residuals_ = standardized_residuals
        self.cooks_distance_ = cooks
        self.local_r2_ = local_r2
        self.diagnostics_ = diagnostics
        if self.store_weights:
            self.spatial_weights_ = spatial
            self.similarity_weights_ = similarity
            self.combined_weights_ = combined
        self._is_fitted = True
    except Exception:
        self._reset_fit_state()
        raise

    if self.verbose:
        print(
            "SGWR fitted: "
            f"bandwidth={self.bandwidth_}, alpha={self.alpha_:.6f}, "
            f"AICc={self.diagnostics_['aicc']:.6f}"
        )
    return self

predict_result

predict_result(
    X: Union[ndarray, DataFrame],
    coords: Union[ndarray, DataFrame],
) -> SGWRPredictionResult

Recalibrate SGWR at new locations and return local parameters.

Source code in src/pygwrx/models/sgwr.py
def predict_result(
    self,
    X: Union[np.ndarray, pd.DataFrame],
    coords: Union[np.ndarray, pd.DataFrame],
) -> SGWRPredictionResult:
    """Recalibrate SGWR at new locations and return local parameters."""
    X_arr, X_design, coords_arr = self._prediction_inputs(X, coords)
    if (
        self.X_design_ is None
        or self.y_train_ is None
        or self.coords_train_ is None
        or self.bandwidth_ is None
        or self.alpha_ is None
    ):
        raise RuntimeError("Training state is incomplete.")

    training_similarity = self._transform_similarity(self.X_train_)
    query_similarity = self._transform_similarity(X_arr)
    spatial = self._spatial_weights(coords_arr, self.coords_train_, self.bandwidth_)
    similarity = self._similarity_weights(query_similarity, training_similarity)
    combined = self._combine_weights(spatial, similarity, self.alpha_)
    local_fit = self._fit_weight_matrix(
        self.X_design_,
        self.y_train_,
        combined,
        X_design,
        compute_covariance=False,
    )
    if self.fit_intercept:
        intercept = local_fit.parameters[:, 0]
        coef = local_fit.parameters[:, 1:]
    else:
        intercept = np.zeros(X_arr.shape[0], dtype=float)
        coef = local_fit.parameters
    return SGWRPredictionResult(
        predictions=local_fit.fitted_values,
        coef=coef,
        intercept=intercept,
        coords=coords_arr.copy(),
        feature_names=self.feature_names_,
    )

predict

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

Predict at new locations using direct SGWR recalibration.

Source code in src/pygwrx/models/sgwr.py
def predict(
    self,
    X: Union[np.ndarray, pd.DataFrame],
    coords: Union[np.ndarray, pd.DataFrame],
) -> np.ndarray:
    """Predict at new locations using direct SGWR recalibration."""
    return self.predict_result(X, coords).predictions

results_frame

results_frame() -> pd.DataFrame

Return training-location parameters, inference, and fitted values.

Source code in src/pygwrx/models/sgwr.py
def results_frame(self) -> pd.DataFrame:
    """Return training-location parameters, inference, and fitted values."""
    self._require_fitted()
    if (
        self.coords_train_ is None
        or self.fitted_values_ is None
        or self.residuals_ is None
        or self.coef_ is None
        or self.coef_se_ is None
        or self.coef_t_ is None
        or self.intercept_ is None
        or self.intercept_se_ is None
        or self.intercept_t_ is None
        or self.local_r2_ is None
        or self.influence_ is None
        or self.cooks_distance_ is None
    ):
        raise RuntimeError("Training results are incomplete.")
    data: Dict[str, np.ndarray] = {
        "coord_0": self.coords_train_[:, 0],
        "coord_1": self.coords_train_[:, 1],
        "fitted": self.fitted_values_,
        "residual": self.residuals_,
        "intercept": self.intercept_,
        "intercept_se": self.intercept_se_,
        "intercept_t": self.intercept_t_,
        "local_r2": self.local_r2_,
        "influence": self.influence_,
        "cooks_distance": self.cooks_distance_,
    }
    for index, name in enumerate(self.feature_names_):
        data[f"coef_{name}"] = self.coef_[:, index]
        data[f"se_{name}"] = self.coef_se_[:, index]
        data[f"t_{name}"] = self.coef_t_[:, index]
    return pd.DataFrame(data)

summary

summary() -> str

Return a plain-text SGWR configuration and diagnostics table.

Source code in src/pygwrx/models/sgwr.py
def summary(self) -> str:
    """Return a plain-text SGWR configuration and diagnostics table."""
    self._require_fitted()
    if self.diagnostics_ is None:
        raise RuntimeError("Diagnostics are unavailable.")
    summary: Dict[str, object] = dict(self.diagnostics_)
    summary.update(
        {
            "n_samples": int(self.y_train_.size),
            "n_features": len(self.feature_names_),
            "feature_names": self.feature_names_,
            "similarity_features": self.similarity_feature_names_,
            "bandwidth": self.bandwidth_,
            "adaptive": self.adaptive,
            "kernel": self.kernel,
            "bandwidth_kernel": self.bandwidth_kernel,
            "alpha": self.alpha_,
            "standardize_similarity": self.standardize_similarity,
            "sigma2": self.sigma2_,
        }
    )
    return format_summary("SGWR Summary", summary)

Runnable examples used on this page

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

"""Fit similarity and geographically weighted regression."""

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

X, y, coords = spatial_regression(n=48, p=3)
model = SGWR(
    bandwidth=24,
    adaptive=True,
    alpha=0.45,
    similarity_vars=["x1", "x2"],
    store_weights=True,
).fit(X, y, coords)
print_model_result(model)
print("combined_weights_shape=", model.combined_weights_.shape)
print("predictions=", model.predict(X.iloc[:3], coords.iloc[:3]))