Skip to content
Merged
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
1 change: 1 addition & 0 deletions doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Implemented Models
* **GWRBasic** — Basic Geographically Weighted Regression with a single bandwidth.
* **GWRMultiscale** — Multiscale GWR (MGWR) with per-variable bandwidths and
backfitting algorithm.
* **GTWR** — Geographically and Temporally Weighted Regression.
* **GWAverage** — Geographically Weighted Summary Statistics (mean, std dev, etc.).
* **GWCorrelation** — Geographically Weighted Correlation coefficients.

Expand Down
1 change: 1 addition & 0 deletions doc/models.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ Regression Models

models/gwr
models/gwr_multiscale
models/gtwr
models/gwss
131 changes: 131 additions & 0 deletions doc/models/gtwr.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
Geographically and Temporally Weighted Regression (GTWR)
=========================================================

.. _gtwr-overview:

Model Overview
--------------

Geographically and Temporally Weighted Regression (GTWR) extends GWR by
incorporating temporal information via a spatio-temporal distance metric.
GTWR assigns a spatio-temporal ratio parameter :math:`\lambda \in [0, 1]`
that balances the influence of spatial proximity versus temporal proximity
on the weighting scheme.

Key features:

- Supports automatic bandwidth selection via CV or AIC criteria.
- Supports OpenMP parallel computation.
- Provides standard errors and diagnostic statistics.

.. _gtwr-constructor:

Constructor
-----------

.. code-block:: python

from pygwmodel import GTWR, BandwidthWeight
from pygwmodel.spatial_weight import CRSSTDistance

algorithm = GTWR(
data, # GeoDataFrame
'PURCHASE', # dependent variable
['FLOORSZ', 'UNEMPLOY', 'PROF'], # independent variables
times='TIME', # temporal column
weight=BandwidthWeight(36.0, adaptive=True),
distance=CRSSTDistance(lambda_=0.5)
)

Parameters:

.. list-table::
:header-rows: 1
:widths: 20 20 60

* - Parameter
- Type
- Description
* - ``sdf``
- ``GeoDataFrame``
- Input data with geometry
* - ``depen_var``
- ``str``
- Dependent variable column name
* - ``indep_vars``
- ``List[str]``
- Independent variable column names
* - ``times``
- ``str``
- Column for temporal stamps
* - ``weight``
- ``BandwidthWeight``
- Bandwidth configuration
* - ``distance``
- ``CRSSTDistance``
- Spatio-temporal distance (default CRSSTDistance)
* - ``has_intercept``
- ``bool``
- Include intercept term (default True)

.. _gtwr-examples:

Code Examples
-------------

Basic Fit
~~~~~~~~~

.. code-block:: python

from pygwmodel import GTWR, BandwidthWeight
from pygwmodel.spatial_weight import CRSSTDistance

algorithm = GTWR(
data, 'PURCHASE', ['FLOORSZ', 'UNEMPLOY', 'PROF'],
times='TIME',
weight=BandwidthWeight(36.0, adaptive=True),
distance=CRSSTDistance(lambda_=0.5)
).fit()

print(algorithm.diagnostic)
# {'RSS': ..., 'AIC': ..., 'AICc': ..., 'RSquare': ...}

print(algorithm.result_layer.columns)
# Index(['Intercept', 'FLOORSZ', 'UNEMPLOY', 'PROF',
# 'Intercept_SE', 'FLOORSZ_SE', 'UNEMPLOY_SE', 'PROF_SE',
# 'fitted'])

Bandwidth Auto-Selection
~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: python

algorithm = GTWR(
data, 'PURCHASE', ['FLOORSZ', 'UNEMPLOY', 'PROF'],
times='TIME',
weight=BandwidthWeight(36.0, adaptive=True),
distance=CRSSTDistance(lambda_=0.5)
).fit(optimize_bandwidth=GTWR.BandwidthSelectionCriterionType.CV)

print(f"Optimal bandwidth: {algorithm.weight.bandwidth}")

Prediction
~~~~~~~~~~

.. code-block:: python

prediction = algorithm.predict(data)
print(prediction.columns)
# Index(['Intercept', 'FLOORSZ', 'UNEMPLOY', 'PROF',
# 'y_hat', 'residual'])

Parallel Execution
~~~~~~~~~~~~~~~~~~

.. code-block:: python

from pygwmodel import GTWR, ParallelType

algorithm = GTWR(..., weight=...)
algorithm.enable_parallel(ParallelType.OpenMP, threads=4).fit()
8 changes: 8 additions & 0 deletions doc/pygwmodel.rst
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@ pygwmodel.gwr_multiscale module
:undoc-members:
:show-inheritance:

pygwmodel.gtwr module
---------------------

.. automodule:: pygwmodel.gtwr
:members:
:undoc-members:
:show-inheritance:

pygwmodel.gwss module
---------------------

Expand Down
1 change: 1 addition & 0 deletions doc/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Currently implemented models:

- **Geographically Weighted Regression (GWR)** — :class:`~pygwmodel.gwr_basic.GWRBasic`
- **Multiscale GWR (MGWR)** — :class:`~pygwmodel.gwr_multiscale.GWRMultiscale`
- **Geographically and Temporally Weighted Regression (GTWR)** — :class:`~pygwmodel.gtwr.GTWR`
- **GW Average** — :class:`~pygwmodel.gwss.GWAverage`
- **GW Correlation** — :class:`~pygwmodel.gwss.GWCorrelation`

Expand Down
2 changes: 1 addition & 1 deletion src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ target_link_libraries(_parallel PRIVATE gwmodel)
nanobind_add_module(_spatial_weight STABLE_ABI spatial_weight.cpp)
target_link_libraries(_spatial_weight PRIVATE gwmodel)

nanobind_add_module(_regression STABLE_ABI common.hpp parallel.hpp base.cpp gwr_basic.cpp gwr_multiscale.cpp regression.cpp)
nanobind_add_module(_regression STABLE_ABI common.hpp parallel.hpp base.cpp gwr_basic.cpp gwr_multiscale.cpp gtwr.cpp regression.cpp)
target_link_libraries(_regression PRIVATE gwmodel)

nanobind_add_module(_analysis STABLE_ABI common.hpp base.cpp parallel.hpp analysis.cpp gwss.cpp)
Expand Down
139 changes: 139 additions & 0 deletions src/gtwr.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#include <nanobind/nanobind.h>
#include <nanobind/stl/vector.h>
#include <nanobind/stl/pair.h>
#include <nanobind/ndarray.h>
#include <GTWR.h>
#include <spatialweight/CRSSTDistance.h>
#include "common.hpp"
#include "parallel.hpp"

namespace nb = nanobind;

struct GTWRHelper : gwm::GTWR
{
void prepare_st_distance()
{
mStdistance = mSpatialWeight.distance<gwm::CRSSTDistance>();
}

void set_times_vec(const arma::vec &times)
{
vTimes = times;
}
};

void init_gtwr(nb::module_& m)
{
nb::class_<GTWRHelper, gwm::GWRBase> _GTWR(m, "_GTWR");

nb::enum_<gwm::GTWR::BandwidthSelectionCriterionType>(_GTWR, "BandwidthSelectionCriterionType")
.value("AIC", gwm::GTWR::BandwidthSelectionCriterionType::AIC)
.value("CV", gwm::GTWR::BandwidthSelectionCriterionType::CV)
.export_values();

_GTWR
.def(nb::init<>())
.def(
"prepare_st_distance",
&GTWRHelper::prepare_st_distance
)
.def_prop_ro(
"select_bandwidth_enabled",
&gwm::GTWR::isAutoselectBandwidth
)
.def(
"set_select_bandwidth",
[](GTWRHelper &instance, bool enable, int criterion)
{
instance.setIsAutoselectBandwidth(enable);
instance.setBandwidthSelectionCriterion(
(gwm::GTWR::BandwidthSelectionCriterionType)criterion);
}
)
.def(
"set_select_lambda",
[](GTWRHelper &instance, bool enable)
{ instance.setIsAutoselectLambda(enable); }
)
.def(
"set_select_lambda_bw",
[](GTWRHelper &instance, bool enable)
{ instance.setIsAutoselectLambdaBw(enable); }
)
.def_prop_rw(
"has_hat_matrix",
&gwm::GTWR::hasHatMatrix,
&gwm::GTWR::setHasHatMatrix
)
.def_prop_ro(
"bandwidth_criterions",
&gwm::GTWR::bandwidthSelectionCriterionList
)
.def(
"set_times_vec",
[](GTWRHelper &instance, const arma::vec &times)
{
instance.set_times_vec(times);
}
)
.def(
"fit",
[](GTWRHelper &instance){ instance.fit(); }
)
.def(
"predict",
[](GTWRHelper &instance, arma::mat locs){ return instance.predict(locs); },
nb::rv_policy::move
)
.def_prop_ro(
"fitted",
[](GTWRHelper &instance){
return instance.Fitted(instance.independentVariables(), instance.betas());
},
nb::rv_policy::move
)
.def(
"predict",
[](gwm::GTWR &instance, arma::mat locs){ return instance.predict(locs); },
nb::rv_policy::move
)
.def_prop_ro(
"betasSE",
&gwm::GTWR::betasSE,
nb::rv_policy::move
)
.def_prop_ro(
"fitted",
[](gwm::GTWR &instance){
return instance.Fitted(instance.independentVariables(), instance.betas());
},
nb::rv_policy::move
)
.def_prop_ro(
"s_hat",
&gwm::GTWR::sHat,
nb::rv_policy::move
)
.def_prop_ro(
"q_diag",
&gwm::GTWR::qDiag,
nb::rv_policy::move
)
.def_prop_ro(
"s",
&gwm::GTWR::s,
nb::rv_policy::move
)
.def_prop_ro(
"lambda_",
&gwm::GTWR::getLambda
)
.def_prop_ro(
"angle",
&gwm::GTWR::getAngle
)
;

def_parallel_info(_GTWR);
def_parallel_openmp(_GTWR);
}
3 changes: 2 additions & 1 deletion src/pygwmodel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ def _add_windows_dll_directories():

from .gwr_basic import GWRBasic, ParallelType
from .gwr_multiscale import GWRMultiscale
from .spatial_weight import SpatialWeight, BandwidthWeight, CRSDistance
from .gtwr import GTWR
from .spatial_weight import SpatialWeight, BandwidthWeight, CRSDistance, CRSSTDistance
from .gwss import GWAverage, GWCorrelation

if __name__ == "__main__":
Expand Down
Loading
Loading