Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/workflows/cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
name: CD

on:
push:
tags: ["v*"]
workflow_dispatch:

jobs:
build-wheels:
name: Build wheels on ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
env:
USERNAME: GWmodel-Lab
FEED_URL: https://nuget.pkg.github.com/GWmodel-Lab/index.json
VCPKG_BINARY_SOURCES: "clear;nuget,github,readwrite"
VCPKG_USE_NUGET_CACHE: 1
permissions:
contents: read
packages: write

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install cibuildwheel
run: pip install cibuildwheel

- name: Restore Vcpkg
if: runner.os == 'Windows'
shell: pwsh
run: |
cd "$env:VCPKG_INSTALLATION_ROOT"
git fetch
git switch 2026.04.27 --detach

- name: Setup NuGet Credentials
if: runner.os == 'Windows'
shell: pwsh
run: |
$VCPKG_EXE="$env:VCPKG_INSTALLATION_ROOT/vcpkg.exe"
$NUGET_EXE="$(.$VCPKG_EXE fetch nuget)"
.$NUGET_EXE sources add -Source ${{ env.FEED_URL }} -Name github -UserName ${{ env.USERNAME }} -Password ${{ secrets.GITHUB_TOKEN }} -StorePasswordInClearText
.$NUGET_EXE setapikey ${{ secrets.GITHUB_TOKEN }} -Source "${{ env.FEED_URL }}"

- name: Build wheels
run: python -m cibuildwheel --output-dir wheelhouse
env:
CIBW_BUILD: "cp312-*"
CIBW_SKIP: "pp* *-musllinux* *-manylinux_i686* *-win32 *-macosx_x86_64"
CIBW_BEFORE_ALL_LINUX: >
apt-get update -qq &&
apt-get install -qq libarmadillo-dev libopenblas-dev libgsl-dev
CIBW_ENVIRONMENT_LINUX: >
CMAKE_ARGS="-DWITH_TESTS=OFF"
PYGW_TEST_DATA="{project}/tests/londonhp100.csv"
CIBW_BEFORE_ALL_MACOS: brew install armadillo gsl openblas
CIBW_ENVIRONMENT_MACOS: >
CMAKE_ARGS="-DWITH_TESTS=OFF"
PYGW_TEST_DATA="{project}/tests/londonhp100.csv"
MACOSX_DEPLOYMENT_TARGET="10.14"
CIBW_BEFORE_ALL_WINDOWS: |
$vcpkg = "$env:VCPKG_INSTALLATION_ROOT/vcpkg.exe"
& $vcpkg install armadillo gsl openblas --triplet x64-windows
CIBW_ENVIRONMENT_WINDOWS: >
PATH="C:/vcpkg/installed/x64-windows/bin"
CMAKE_ARGS="-DWITH_TESTS=OFF -DCMAKE_TOOLCHAIN_FILE=C:/vcpkg/scripts/buildsystems/vcpkg.cmake"
PYGW_TEST_DATA="{project}/tests/londonhp100.csv"
CIBW_TEST_COMMAND: "pytest {project}/tests -v --ignore={project}/tests/test_gwss.py"
CIBW_TEST_REQUIRES: pytest

- name: Upload wheels
uses: actions/upload-artifact@v4
with:
name: wheels-${{ matrix.os }}
path: wheelhouse/*.whl

publish-pypi:
name: Publish to PyPI
needs: build-wheels
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
id-token: write

steps:
- name: Download wheels
uses: actions/download-artifact@v4
with:
pattern: wheels-*
path: dist
merge-multiple: true

- name: Publish to PyPI
uses: pypa/gh-action-pypi-publish@release/v1

github-release:
name: Create GitHub Release
needs: build-wheels
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')
permissions:
contents: write

steps:
- name: Download wheels
uses: actions/download-artifact@v4
with:
pattern: wheels-*
path: dist
merge-multiple: true

- name: Create Release
uses: softprops/action-gh-release@v2
with:
files: dist/*.whl
generate_release_notes: true
113 changes: 113 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
name: CI

on:
push:
branches: [main, master]
pull_request:
branches: [main, master]
workflow_dispatch:

jobs:
test-unix:
name: ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Install native dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update -qq
sudo apt-get install -qq libarmadillo-dev libopenblas-dev libgsl-dev

- name: Install native dependencies (macOS)
if: runner.os == 'macOS'
run: brew install armadillo gsl openblas

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install build dependencies
run: pip install nanobind "scikit-build-core[pyproject]" numpy geopandas pytest

- name: Build package
run: pip install --no-build-isolation -ve .
env:
CMAKE_ARGS: "-DWITH_TESTS=OFF"

- name: Run tests
run: python -m pytest test/ -v --ignore=test/test_gwss.py
env:
PYGW_TEST_DATA: test/londonhp100.csv

test-windows:
name: windows-latest
runs-on: windows-latest
env:
USERNAME: GWmodel-Lab
FEED_URL: https://nuget.pkg.github.com/GWmodel-Lab/index.json
VCPKG_BINARY_SOURCES: "clear;nuget,github,readwrite"
VCPKG_USE_NUGET_CACHE: 1
permissions:
contents: read
packages: write

steps:
- name: Checkout
uses: actions/checkout@v4
with:
submodules: recursive

- name: Restore Vcpkg
shell: pwsh
run: |
cd "$env:VCPKG_INSTALLATION_ROOT"
git fetch
git switch 2026.04.27 --detach

- name: Setup NuGet Credentials
shell: pwsh
run: |
$VCPKG_EXE="$env:VCPKG_INSTALLATION_ROOT/vcpkg.exe"
$NUGET_EXE="$(.$VCPKG_EXE fetch nuget)"
.$NUGET_EXE sources add -Source ${{ env.FEED_URL }} -Name github -UserName ${{ env.USERNAME }} -Password ${{ secrets.GITHUB_TOKEN }} -StorePasswordInClearText
.$NUGET_EXE setapikey ${{ secrets.GITHUB_TOKEN }} -Source "${{ env.FEED_URL }}"

- name: Install native dependencies
shell: pwsh
run: |
$vcpkg = "$env:VCPKG_INSTALLATION_ROOT/vcpkg.exe"
& $vcpkg install armadillo gsl openblas[threads] --triplet x64-windows

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install build dependencies
run: pip install nanobind "scikit-build-core[pyproject]" numpy geopandas pytest

- name: Build package
shell: pwsh
run: |
$env:CMAKE_ARGS = "-DWITH_TESTS=OFF -DCMAKE_TOOLCHAIN_FILE=$env:VCPKG_INSTALLATION_ROOT/scripts/buildsystems/vcpkg.cmake"
pip install --no-build-isolation -v .

- name: Add vcpkg DLLs to PATH
shell: pwsh
run: echo "$env:VCPKG_INSTALLATION_ROOT\installed\x64-windows\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append

- name: Run tests
run: python -m pytest test/ -v --ignore=test/test_gwss.py
env:
PYGW_TEST_DATA: test/londonhp100.csv
19 changes: 19 additions & 0 deletions .readthedocs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Read the Docs configuration file
# https://docs.readthedocs.io/en/stable/config-file/v2.html

version: 2

build:
os: ubuntu-24.04
tools:
python: "3.12"
jobs:
post_checkout:
- git submodule update --init --recursive

sphinx:
configuration: doc/conf.py

python:
install:
- requirements: doc/requirements.txt
21 changes: 18 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,26 @@ pip install . --config-settings=cmake.args=-DBLA_VENDOR=OpenBLAS
## Getting started

```py
from pygwmodel import GWRBasic
algorithm = GWRBasic(data, y, x, 36.0).fit()
from pygwmodel import GWRBasic, BandwidthWeight, CRSDistance
algorithm = GWRBasic(data, y, x,
weight=BandwidthWeight(36.0, adaptive=True),
distance=CRSDistance()).fit()
```

For full usage, please see the unit tests in `test` directory.
Multiscale GWR (MGWR) assigns a separate bandwidth to each predictor:

```py
from pygwmodel import GWRMultiscale, BandwidthWeight

n_var = 4 # intercept + 3 predictors
weights = [BandwidthWeight(36.0, adaptive=True) for _ in range(n_var)]

algorithm = GWRMultiscale(data, y, x, weights=weights).fit()
print(algorithm.diagnostic)
```

For full usage, please see the unit tests in `test` directory and the
`documentation <https://gwmodel-lab.github.io/pygwmodel/>`_.

## Related work

Expand Down
22 changes: 22 additions & 0 deletions doc/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,28 @@
'sphinx.ext.autodoc'
]

# Mock C++ extension modules for ReadTheDocs (or other environments
# where the native libraries cannot be built).
autodoc_mock_imports = [
'pygwmodel._spatial_weight',
'pygwmodel._parallel',
'pygwmodel._regression',
'pygwmodel._analysis',
]

# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']

# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used for content translation via gettext catalogs.
# Usually you set "language" from the command line.
language = 'en'

# Locale directories for gettext-based translation
locale_dirs = ['locale/']

# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
Expand All @@ -48,6 +67,9 @@
'libgwmodel'
]

# Suppress strict reference-consistency check across translations
suppress_warnings = ['i18n.inconsistent_references']


# -- Options for HTML output -------------------------------------------------

Expand Down
55 changes: 40 additions & 15 deletions doc/index.rst
Original file line number Diff line number Diff line change
@@ -1,28 +1,53 @@
.. pygwmodel documentation master file, created by
sphinx-quickstart on Sat Nov 18 16:45:14 2023.
You can adapt this file completely to your liking, but it should at least
contain the root `toctree` directive.
pygwmodel Documentation
=========================

Welcome to pygwmodel's documentation!
=====================================
**pygwmodel** is a Python package providing conscious and easy-to-use interfaces to
high-performance C++ implementations of geographically weighted (GW) models,
based on `libgwmodel <https://github.com/GWmodel-Lab/libgwmodel>`_ and **GeoPandas**.

The **pygwmodel** is a Python package of bindings to geographically weighted (GW) models.
GW modelling is a special branch of spatial statistics.
GW models suit situations when data are not described well by some global model,
but where there are spatial regions where a suitably localized calibration provides a better description.
GW models are a branch of spatial statistics suited to situations where data are
not well described by some global model, but where spatial regions exist where a
suitably localized calibration provides a better description.

The goal of **pygwmodel** is to provide conscious and easy-to-use user interface
to high-performance C++ implementations of GW models based on **GeoPandas**.
We believe with the newly designed interfaces and the underlying C++ core,
users will get fluent experiences.
Implemented Models
------------------

* **GWRBasic** — Basic Geographically Weighted Regression with a single bandwidth.
* **GWRMultiscale** — Multiscale GWR (MGWR) with per-variable bandwidths and
backfitting algorithm.
* **GWSS** — Geographically Weighted Summary Statistics (averages and correlations).

Quick Start
-----------

.. code-block:: python

from pygwmodel import GWRBasic, GWRMultiscale, BandwidthWeight, CRSDistance

# Basic GWR
algorithm = GWRBasic(data, y, x,
weight=BandwidthWeight(36.0, adaptive=True),
distance=CRSDistance()).fit()
print(algorithm.diagnostic['RSquare'])

# Multiscale GWR
mgwr = GWRMultiscale(data, y, x,
weights=[BandwidthWeight(36.0, adaptive=True) for _ in range(4)]
).fit()
print(mgwr.diagnostic)

.. toctree::
:maxdepth: 2
:caption: Contents:

quickstart
spatial_weight
models
models/gwss
performance
modules.rst

Indices and tables
Indices and Tables
==================

* :ref:`genindex`
Expand Down
Loading
Loading