Skip to content

Model comparison

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

compare_coefficient_surfaces

Compare the same local coefficient across two or more fitted models.

Property Value
Type function
Import from pygwrx.plotting import compare_coefficient_surfaces
Signature compare_coefficient_surfaces(models: 'Sequence[object]', feature, *, geometry=None, labels: 'Optional[Sequence[str]]' = None, significance: 'Optional[str]' = None, alpha: 'float' = 0.05, shared_scale: 'bool' = True, theme: 'str' = 'default', figsize: 'Optional[Tuple[float, float]]' = None)
Maintained example examples/plotting/02_diagnostics_and_comparison.py

compare_coefficient_surfaces

compare_coefficient_surfaces(
    models: Sequence[object],
    feature,
    *,
    geometry=None,
    labels: Optional[Sequence[str]] = None,
    significance: Optional[str] = None,
    alpha: float = 0.05,
    shared_scale: bool = True,
    theme: str = "default",
    figsize: Optional[Tuple[float, float]] = None
)

Compare the same local coefficient across two or more fitted models.

Source code in src/pygwrx/plotting/comparison.py
def compare_coefficient_surfaces(
    models: Sequence[object],
    feature,
    *,
    geometry=None,
    labels: Optional[Sequence[str]] = None,
    significance: Optional[str] = None,
    alpha: float = 0.05,
    shared_scale: bool = True,
    theme: str = "default",
    figsize: Optional[Tuple[float, float]] = None,
):
    """Compare the same local coefficient across two or more fitted models."""
    model_list = list(models)
    if len(model_list) < 2:
        raise ValueError("models must contain at least two fitted estimators.")
    if labels is None:
        model_labels = [model.__class__.__name__ for model in model_list]
    else:
        model_labels = [str(label) for label in labels]
        if len(model_labels) != len(model_list):
            raise ValueError("labels must contain one entry per model.")

    values = [parameter_view(model, feature).values for model in model_list]
    if len({array.size for array in values}) != 1:
        raise ValueError("All models must contain the same number of calibration rows.")
    combined = np.concatenate(values)
    finite = combined[np.isfinite(combined)]
    if finite.size == 0:
        raise ValueError("Coefficient surfaces contain no finite values.")
    lower = float(np.min(finite)) if shared_scale else None
    upper = float(np.max(finite)) if shared_scale else None
    if shared_scale and lower < 0.0 < upper:
        extent = max(abs(lower), abs(upper))
        lower, upper = -extent, extent

    with plotting_theme(theme):
        n = len(model_list)
        fig, axes = plt.subplots(
            1,
            n,
            figsize=figsize or (4.8 * n, 4.4),
            squeeze=False,
        )
        for index, (model, label) in enumerate(zip(model_list, model_labels)):
            plot_coefficient_map(
                model,
                feature,
                geometry=geometry,
                significance=significance,
                alpha=alpha,
                theme=theme,
                ax=axes[0, index],
                vmin=lower,
                vmax=upper,
                title=label,
            )
        fig.tight_layout()
        return fig, axes.reshape(-1)

compare_model_diagnostics

Compare normalized global diagnostics across fitted models.

Property Value
Type function
Import from pygwrx.plotting import compare_model_diagnostics
Signature compare_model_diagnostics(models: 'Sequence[object]', *, metrics: 'Sequence[str]' = ('r2', 'rmse', 'aicc', 'enp'), labels: 'Optional[Sequence[str]]' = None, theme: 'str' = 'default', figsize: 'Optional[Tuple[float, float]]' = None)
Maintained example examples/plotting/02_diagnostics_and_comparison.py

compare_model_diagnostics

compare_model_diagnostics(
    models: Sequence[object],
    *,
    metrics: Sequence[str] = ("r2", "rmse", "aicc", "enp"),
    labels: Optional[Sequence[str]] = None,
    theme: str = "default",
    figsize: Optional[Tuple[float, float]] = None
)

Compare normalized global diagnostics across fitted models.

Source code in src/pygwrx/plotting/comparison.py
def compare_model_diagnostics(
    models: Sequence[object],
    *,
    metrics: Sequence[str] = ("r2", "rmse", "aicc", "enp"),
    labels: Optional[Sequence[str]] = None,
    theme: str = "default",
    figsize: Optional[Tuple[float, float]] = None,
):
    """Compare normalized global diagnostics across fitted models."""
    from pygwrx.diagnostics import diagnostics_frame

    frame = diagnostics_frame(models, labels=labels)
    selected = [str(metric) for metric in metrics if str(metric) in frame.columns]
    if not selected:
        raise ValueError(
            "None of the requested metrics are available on the fitted models."
        )
    with plotting_theme(theme):
        fig, axes = plt.subplots(
            1,
            len(selected),
            figsize=figsize or (4.2 * len(selected), 4.0),
            squeeze=False,
        )
        for axis, metric in zip(axes.flat, selected):
            values = frame[metric].astype(float)
            axis.bar(frame.index.astype(str), values)
            axis.set_title(metric.replace("_", " ").upper())
            axis.tick_params(axis="x", rotation=30)
            axis.grid(True, axis="y", alpha=0.22)
        fig.tight_layout()
        return fig, axes.reshape(-1)

Runnable examples used on this page

examples/plotting/02_diagnostics_and_comparison.py
# SPDX-FileCopyrightText: 2026 Jinghao Hu
# SPDX-License-Identifier: MIT

"""All general residual, bandwidth, comparison, and collinearity plots."""

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

matplotlib.use("Agg", force=True)
from _common import save_plot
from _models import surface_models

from pygwrx.plotting import (
    compare_coefficient_surfaces,
    compare_model_diagnostics,
    plot_bandwidth_selection,
    plot_coefficient_variability,
    plot_diagnostic_panel,
    plot_kernel_weights,
    plot_local_collinearity,
    plot_local_diagnostics,
    plot_mgwr_bandwidths,
    plot_observed_vs_predicted,
    plot_qq,
    plot_residual_histogram,
    plot_residuals,
    plot_spatial_residuals,
)

X, y, coords, gwr, mgwr, lcr = surface_models()
plots = {
    "compare_surfaces.png": compare_coefficient_surfaces([gwr, mgwr], "x1"),
    "compare_diagnostics.png": compare_model_diagnostics([gwr, mgwr]),
    "kernel_weights.png": plot_kernel_weights(gwr, focus=3),
    "mgwr_bandwidths.png": plot_mgwr_bandwidths(mgwr),
    "residuals.png": plot_residuals(gwr.fitted_values_, gwr.residuals_),
    "residual_histogram.png": plot_residual_histogram(gwr.residuals_),
    "qq.png": plot_qq(gwr.residuals_),
    "spatial_residuals.png": plot_spatial_residuals(coords, gwr.residuals_),
    "observed_predicted.png": plot_observed_vs_predicted(y, gwr.fitted_values_),
    "bandwidth_selection.png": plot_bandwidth_selection(
        [10, 15, 20, 25], [14.0, 9.0, 7.5, 8.2], 20, criterion="AICc"
    ),
    "coefficient_variability.png": plot_coefficient_variability(
        gwr.coef_, feature_names=["x1", "x2"]
    ),
    "diagnostic_panel_arrays.png": plot_diagnostic_panel(
        y, gwr.fitted_values_, gwr.residuals_, coords
    ),
    "diagnostic_panel_model.png": plot_diagnostic_panel(gwr),
    "local_diagnostics.png": plot_local_diagnostics(
        coords, {"local_r2": gwr.local_r2_, "influence": gwr.influence_}
    ),
    "collinearity_gwr.png": plot_local_collinearity(gwr, "condition_number"),
    "collinearity_lcr.png": plot_local_collinearity(lcr, "local_lambda"),
}
for name, result in plots.items():
    print(save_plot(result, name))