Skip to content

Core numerical functions

The core layer contains the reusable numerical building blocks behind the model classes. Most users should start with a model class; use these functions directly when developing a new estimator, reproducing a formula, or testing one numerical component in isolation.

Functional map

Area What it provides Typical use
Kernels Gaussian, bisquare, exponential, tricube, boxcar Convert distance to local weight
Distances Euclidean, Manhattan, Chebyshev, Minkowski, haversine Define the neighbourhood geometry
Bandwidth selection CV, AIC, BIC selectors Select distance or neighbour-count scale
Optimization Golden-section and Brent search Optimize scalar criteria
Solvers Weighted least squares, local regression, hat matrix Build custom local estimators
Metrics R², information criteria, ENP/EDF, trace statistics Diagnose fitted smoothers
Validation/base Input contracts and estimator base classes Extend pyGWRx safely

Warning

Core functions expose lower-level contracts. Check shapes, intercept handling, rank, and fixed/adaptive bandwidth semantics explicitly.

Kernels

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Evaluate every public kernel and resolve kernels by name or callable."""

import numpy as np
from pygwrx.core import (
    bisquare_kernel,
    boxcar_kernel,
    exponential_kernel,
    gaussian_kernel,
    get_kernel_function,
    tricube_kernel,
)

distances = np.array([0.0, 0.5, 1.0, 2.0])
for kernel in (
    gaussian_kernel,
    bisquare_kernel,
    exponential_kernel,
    tricube_kernel,
    boxcar_kernel,
):
    print(kernel.__name__, kernel(distances, bandwidth=1.5))
print("resolved=", get_kernel_function("bisquare").__name__)
print("callable_passthrough=", get_kernel_function(gaussian_kernel) is gaussian_kernel)

Distances and validation

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Use all public distance, validation, caching, and chunk helpers."""

import numpy as np
import pandas as pd
from pygwrx.core import (
    DistanceCache,
    add_intercept,
    chebyshev_distance,
    chunked_computation,
    compute_distance_matrix,
    euclidean_distance,
    haversine_distance,
    manhattan_distance,
    minkowski_distance,
    validate_coords,
    validate_data,
)

a = np.array([[0.0, 0.0], [1.0, 2.0]])
b = np.array([[2.0, 1.0], [3.0, 4.0]])
print("euclidean=", euclidean_distance(a, b))
print("manhattan=", manhattan_distance(a, b))
print("chebyshev=", chebyshev_distance(a, b))
print("minkowski_p3=", minkowski_distance(a, b, p=3.0))
print(
    "haversine_km=",
    haversine_distance(np.array([[116.4, 39.9]]), np.array([[121.5, 31.2]])),
)
print("matrix=", compute_distance_matrix(a, metric="euclidean"))

X, y = validate_data(pd.DataFrame({"x": [1, 2]}), pd.Series([3, 4]))
coords = validate_coords(pd.DataFrame(a, columns=["x", "y"]))
print("validated_shapes=", X.shape, y.shape, coords.shape)
print("with_intercept=", add_intercept(X))
print("chunks=", list(chunked_computation(10, chunk_size=4)))
print("cache_memory=", DistanceCache.estimate_memory(100, 50))
print("cache_strategy=", DistanceCache.get_strategy(100, 50, task="gwr"))
print("should_cache=", DistanceCache.should_cache(100, 50))
DistanceCache.print_recommendation(100, 50)

GeoPandas coordinates

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Extract coordinates from a GeoDataFrame using the base installation."""

import geopandas as gpd
from shapely.geometry import Point
from pygwrx.core import extract_geopandas_coords

gdf = gpd.GeoDataFrame(
    {"name": ["a", "b"]},
    geometry=[Point(0.0, 1.0), Point(2.0, 3.0)],
    crs="EPSG:3857",
)
print(extract_geopandas_coords(gdf))

Local solvers

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Run all public local-regression solver utilities."""

import numpy as np
from pygwrx.core import (
    adaptive_bandwidth_weights,
    compute_hat_matrix,
    gaussian_kernel,
    local_regression,
    weighted_least_squares,
)

rng = np.random.default_rng(0)
coords = rng.uniform(0.0, 5.0, size=(20, 2))
x = rng.normal(size=20)
X = np.column_stack((np.ones(20), x))
y = 1.0 + 2.0 * x + rng.normal(0.0, 0.05, 20)
distances = np.linalg.norm(coords - coords[0], axis=1)
weights = gaussian_kernel(distances, bandwidth=2.0)
beta, covariance = weighted_least_squares(X, y, weights)
print("beta=", beta)
print("covariance_shape=", covariance.shape)
print("adaptive_scale=", adaptive_bandwidth_weights(distances, 8))
print(
    "local_parameters=",
    local_regression(X, y, coords, coords[:3], gaussian_kernel, 2.0),
)
hat = compute_hat_matrix(X, coords, gaussian_kernel, 2.0)
print("hat_shape_trace=", hat.shape, np.trace(hat))

Metrics

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Calculate every public model-fit and effective-parameter metric."""

import numpy as np
from pygwrx.core import (
    compute_adjusted_r_squared,
    compute_aic,
    compute_aicc,
    compute_bic,
    compute_diagnostics,
    compute_edf,
    compute_effective_parameters,
    compute_enp,
    compute_local_r_squared,
    compute_r_squared,
    compute_trace_statistics,
)

y = np.array([1.0, 2.0, 2.8, 4.2, 5.0])
yhat = np.array([1.1, 1.9, 3.0, 4.0, 4.9])
hat = np.eye(5) * 0.4
weights = np.vstack([np.linspace(1.0, 0.2, 5)] * 5)
trace = compute_trace_statistics(hat)
print("r2=", compute_r_squared(y, yhat))
print("adjusted_r2=", compute_adjusted_r_squared(y, yhat, edf=3.0))
print("aic=", compute_aic(y, yhat, n_params=2.0))
print("aicc=", compute_aicc(y, yhat, n_params=2.0))
print("bic=", compute_bic(y, yhat, trace_S=2.0))
print("local_r2=", compute_local_r_squared(y, yhat, weights))
print("effective_parameters=", compute_effective_parameters(hat))
print("trace_statistics=", trace)
print("edf=", compute_edf(5, trace["trace_S"], trace["trace_StS"]))
print("enp=", compute_enp(trace["trace_S"], trace["trace_StS"]))
print(
    "diagnostics=",
    compute_diagnostics(y, yhat, hat, n_features=1, compute_gwr_stats=True),
)

Optimization

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Use both public scalar optimizers and the OptimizationResult container."""

from pygwrx.core import BrentSearch, GoldenSectionSearch, OptimizationResult


def objective(x):
    """Simple convex objective with a known minimum."""
    return (x - 2.5) ** 2 + 1.0


golden = GoldenSectionSearch(tol=1e-7, max_iter=100, verbose=False)
brent = BrentSearch(tol=1e-7, max_iter=100, verbose=False)
print("golden=", golden.minimize(objective, 0.0, 5.0))
print("brent=", brent.minimize(objective, 0.0, 5.0))
print("manual_result=", OptimizationResult(2.5, 1.0, 10, True, evaluations=12))

Bandwidth selectors

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Select bandwidths with CV, AIC/AICc, and BIC selectors."""

import numpy as np
from pygwrx.core import (
    AICSelector,
    BICSelector,
    BandwidthSelector,
    CrossValidationSelector,
    gaussian_kernel,
    get_bandwidth_selector,
)
from _common import spatial_regression

X, y, coords = spatial_regression(n=28, p=2)
Xa, ya, ca = X.to_numpy(), np.asarray(y), coords.to_numpy()
selectors = [
    CrossValidationSelector(n_intervals=5, adaptive=True, verbose=False),
    AICSelector(n_intervals=5, corrected=False, adaptive=True, verbose=False),
    AICSelector(n_intervals=5, corrected=True, adaptive=True, verbose=False),
    BICSelector(n_intervals=5, adaptive=True, verbose=False),
]
for selector in selectors:
    print(
        type(selector).__name__,
        selector.select(Xa, ya, ca, gaussian_kernel, bandwidth_range=(10, 18)),
    )
print("factory=", type(get_bandwidth_selector("aicc", adaptive=True)).__name__)
print("abstract_base=", BandwidthSelector)

Base classes

The maintained example below exercises the public symbols in this group. Open source.

# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""Implement minimal concrete estimators from every public base class/mixin."""

from __future__ import annotations

import numpy as np
import pandas as pd

from pygwrx.core import (
    BaseGWR,
    BaseMultiscaleRegressor,
    BaseSpatialClassifier,
    BaseSpatialEstimator,
    BaseSpatialInference,
    BaseSpatialRegressor,
    BaseSpatialStatistics,
    BaseSpatialTransformer,
    BaseSpatiotemporalRegressor,
    MultiscaleMixin,
    SpatiotemporalMixin,
)


class MeanRegressor(BaseSpatialRegressor):
    """Minimal concrete spatial regressor for base-contract demonstration."""

    def fit(self, X, y, coords, **kwargs):
        Xa, ya, ca = self._validate_inputs(X, y, coords)
        self._store_training_data(Xa, ya, ca)
        self.mean_ = float(np.mean(ya))
        self._mark_fitted()
        return self

    def predict(self, X, coords, **kwargs):
        Xa, _ = self._validate_prediction_inputs(X, coords)
        return np.full(Xa.shape[0], self.mean_)


class TinyGWR(BaseGWR):
    """Minimal concrete GWR-style regressor."""

    def fit(self, X, y, coords, **kwargs):
        Xa, ya, ca = self._validate_inputs(X, y, coords)
        self._store_training_data(Xa, ya, ca)
        self.bandwidth_ = 1.0
        self.coef_ = np.zeros_like(Xa)
        self.intercept_ = np.full(len(ya), ya.mean())
        self.fitted_values_ = self.intercept_.copy()
        self.residuals_ = ya - self.fitted_values_
        self._mark_fitted()
        return self

    def predict(self, X, coords, **kwargs):
        Xa, _ = self._validate_prediction_inputs(X, coords)
        return np.full(len(Xa), self.y_train_.mean())


class TimeRegressor(SpatiotemporalMixin, MeanRegressor):
    """Concrete demonstration of the spatiotemporal mixin."""


class MultiRegressor(MultiscaleMixin, MeanRegressor):
    """Concrete demonstration of the multiscale mixin."""


class ConcreteSTR(BaseSpatiotemporalRegressor, MeanRegressor):
    """Concrete base spatiotemporal regressor."""

    fit = MeanRegressor.fit
    predict = MeanRegressor.predict


class ConcreteMSR(BaseMultiscaleRegressor, MeanRegressor):
    """Concrete base multiscale regressor."""

    fit = MeanRegressor.fit
    predict = MeanRegressor.predict


class MajorityClassifier(BaseSpatialClassifier):
    """Minimal majority-class spatial classifier."""

    def fit(self, X, y, coords, **kwargs):
        self._validate_spatial_inputs(X, coords, reset=True)
        values, counts = np.unique(y, return_counts=True)
        self.classes_ = values
        self.majority_ = values[np.argmax(counts)]
        self._mark_fitted()
        return self

    def predict(self, X, coords, **kwargs):
        Xa, _ = self._validate_spatial_inputs(X, coords, reset=False)
        return np.repeat(self.majority_, len(Xa))

    def predict_proba(self, X, coords, **kwargs):
        prediction = self.predict(X, coords)
        return np.column_stack(
            [prediction == class_value for class_value in self.classes_]
        ).astype(float)


class IdentityTransformer(BaseSpatialTransformer):
    """Minimal identity spatial transformer."""

    def fit(self, X, coords, **kwargs):
        self._validate_spatial_inputs(X, coords, reset=True)
        self._mark_fitted()
        return self

    def transform(self, X, coords, **kwargs):
        Xa, _ = self._validate_spatial_inputs(X, coords, reset=False)
        return Xa


class ColumnStatistics(BaseSpatialStatistics):
    """Minimal column-mean spatial statistics estimator."""

    def fit(self, X, coords, **kwargs):
        Xa, ca = self._validate_spatial_inputs(X, coords, reset=True)
        self.means_ = Xa.mean(axis=0)
        self.coords_train_ = ca
        self._mark_fitted()
        return self

    def to_frame(self):
        self._check_is_fitted()
        return pd.DataFrame({"mean": self.means_})


class BasicInference(BaseSpatialInference):
    """Minimal inference result container."""

    def fit(self, X, y, coords, **kwargs):
        self.n_samples_ = len(y)
        self._mark_fitted()
        return self

    def summary(self):
        self._check_is_fitted()
        return f"n_samples={self.n_samples_}"


X = pd.DataFrame({"x": [0.0, 1.0, 2.0, 3.0]})
y = np.array([1.0, 2.0, 2.0, 3.0])
coords = np.column_stack((np.arange(4), np.zeros(4)))

for model in (
    MeanRegressor(),
    TinyGWR(bandwidth=1.0),
    TimeRegressor(),
    MultiRegressor(),
    ConcreteSTR(),
    ConcreteMSR(),
):
    model.fit(X, y, coords)
    print(type(model).__name__, model.predict(X.iloc[:2], coords[:2]))

print("root_estimator_params=", {"contract": "task-specific spatial estimator"})
classifier = MajorityClassifier().fit(X, np.array([0, 1, 1, 1]), coords)
print("classifier=", classifier.predict(X, coords), classifier.predict_proba(X, coords))
print("transformer=", IdentityTransformer().fit(X, coords).transform(X, coords))
print("statistics=", ColumnStatistics().fit(X, coords).to_frame())
print("inference=", BasicInference().fit(X, y, coords).summary())

API

See the Core API reference, where every symbol includes its full docstring, signature, and mapped runnable example.

Regression base hierarchy

BaseSpatialRegressor is the single shared base for the geographically weighted regression family. It owns the common regression state, kernel and bandwidth configuration, local parameter computation, prediction validation, and result export behavior. BaseGWR remains available as an identity alias for backward compatibility with pyGWRx 0.1.2 code.