geometrics 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- geometrics-0.1.0/.gitignore +29 -0
- geometrics-0.1.0/LICENSE +21 -0
- geometrics-0.1.0/PKG-INFO +159 -0
- geometrics-0.1.0/README.md +114 -0
- geometrics-0.1.0/pyproject.toml +177 -0
- geometrics-0.1.0/src/geometrics/__init__.py +147 -0
- geometrics-0.1.0/src/geometrics/_common.py +193 -0
- geometrics-0.1.0/src/geometrics/_data_dict.py +369 -0
- geometrics-0.1.0/src/geometrics/_geo.py +641 -0
- geometrics-0.1.0/src/geometrics/_impacts.py +580 -0
- geometrics-0.1.0/src/geometrics/_labels.py +219 -0
- geometrics-0.1.0/src/geometrics/_mapping.py +641 -0
- geometrics-0.1.0/src/geometrics/_panel.py +179 -0
- geometrics-0.1.0/src/geometrics/_roles.py +143 -0
- geometrics-0.1.0/src/geometrics/_theme.py +410 -0
- geometrics-0.1.0/src/geometrics/_types.py +939 -0
- geometrics-0.1.0/src/geometrics/_validation.py +192 -0
- geometrics-0.1.0/src/geometrics/clubs.py +1044 -0
- geometrics-0.1.0/src/geometrics/convergence.py +1545 -0
- geometrics-0.1.0/src/geometrics/data/__init__.py +262 -0
- geometrics-0.1.0/src/geometrics/data/_registry.py +82 -0
- geometrics-0.1.0/src/geometrics/data/india32_dict.csv +7 -0
- geometrics-0.1.0/src/geometrics/data/india520_dict.csv +29 -0
- geometrics-0.1.0/src/geometrics/dependence.py +926 -0
- geometrics-0.1.0/src/geometrics/distribution_dynamics.py +858 -0
- geometrics-0.1.0/src/geometrics/gwr.py +909 -0
- geometrics-0.1.0/src/geometrics/maps.py +491 -0
- geometrics-0.1.0/src/geometrics/pedagogy/__init__.py +32 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_format.py +88 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/__init__.py +63 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_convergence.py +264 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_dependence.py +207 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_dynamics.py +211 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_gwr.py +161 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_inequality.py +226 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_maps.py +70 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_shared.py +16 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_spacetime.py +201 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_spatial_models.py +319 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_weights.py +86 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_mixin.py +44 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_registry.py +124 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/__init__.py +25 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/convergence.py +201 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/correlation.py +77 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/dynamics.py +157 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/inequality.py +124 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/models.py +326 -0
- geometrics-0.1.0/src/geometrics/pedagogy/_text/spatial.py +263 -0
- geometrics-0.1.0/src/geometrics/py.typed +0 -0
- geometrics-0.1.0/src/geometrics/regional_inequality.py +985 -0
- geometrics-0.1.0/src/geometrics/spacetime.py +665 -0
- geometrics-0.1.0/src/geometrics/spatial_models.py +1349 -0
- geometrics-0.1.0/src/geometrics/weights.py +578 -0
- geometrics-0.1.0/tests/conftest.py +109 -0
- geometrics-0.1.0/tests/fixtures/make_fixtures.py +98 -0
- geometrics-0.1.0/tests/fixtures/mini520.dta +0 -0
- geometrics-0.1.0/tests/fixtures/mini520.geojson +13 -0
- geometrics-0.1.0/tests/test_clubs.py +306 -0
- geometrics-0.1.0/tests/test_convergence.py +458 -0
- geometrics-0.1.0/tests/test_data.py +268 -0
- geometrics-0.1.0/tests/test_data_dict.py +125 -0
- geometrics-0.1.0/tests/test_dependence.py +451 -0
- geometrics-0.1.0/tests/test_dynamics.py +345 -0
- geometrics-0.1.0/tests/test_geo.py +433 -0
- geometrics-0.1.0/tests/test_gwr.py +321 -0
- geometrics-0.1.0/tests/test_impacts.py +439 -0
- geometrics-0.1.0/tests/test_inequality.py +395 -0
- geometrics-0.1.0/tests/test_labels.py +80 -0
- geometrics-0.1.0/tests/test_maps.py +422 -0
- geometrics-0.1.0/tests/test_panel.py +59 -0
- geometrics-0.1.0/tests/test_paper_parity.py +134 -0
- geometrics-0.1.0/tests/test_pedagogy.py +68 -0
- geometrics-0.1.0/tests/test_roles.py +170 -0
- geometrics-0.1.0/tests/test_spacetime.py +368 -0
- geometrics-0.1.0/tests/test_spatial_models.py +445 -0
- geometrics-0.1.0/tests/test_theme.py +113 -0
- geometrics-0.1.0/tests/test_validation.py +95 -0
- geometrics-0.1.0/tests/test_weights.py +274 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*.egg-info/
|
|
5
|
+
dist/
|
|
6
|
+
build/
|
|
7
|
+
.venv/
|
|
8
|
+
.python-version.local
|
|
9
|
+
|
|
10
|
+
# Tooling caches
|
|
11
|
+
.pytest_cache/
|
|
12
|
+
.ruff_cache/
|
|
13
|
+
.mypy_cache/
|
|
14
|
+
.coverage
|
|
15
|
+
coverage.xml
|
|
16
|
+
htmlcov/
|
|
17
|
+
|
|
18
|
+
# Quarto (keep committed _freeze execution results; ignore rendered site)
|
|
19
|
+
docs/_site/
|
|
20
|
+
docs/.quarto/
|
|
21
|
+
docs/reference/
|
|
22
|
+
!docs/reference/_sidebar.yml
|
|
23
|
+
/.quarto/
|
|
24
|
+
|
|
25
|
+
# Jupyter
|
|
26
|
+
.ipynb_checkpoints/
|
|
27
|
+
|
|
28
|
+
# OS
|
|
29
|
+
.DS_Store
|
geometrics-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Carlos Mendez
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: geometrics
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Regional growth, convergence, and inequality analysis on the PySAL stack.
|
|
5
|
+
Project-URL: Homepage, https://github.com/quarcs-lab/geometrics
|
|
6
|
+
Project-URL: Documentation, https://quarcs-lab.github.io/geometrics/
|
|
7
|
+
Project-URL: Repository, https://github.com/quarcs-lab/geometrics
|
|
8
|
+
Project-URL: Issues, https://github.com/quarcs-lab/geometrics/issues
|
|
9
|
+
Author: Carlos Mendez
|
|
10
|
+
License: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: convergence,geopandas,inequality,plotly,pysal,regional science,spatial analysis,spatial econometrics
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: GIS
|
|
22
|
+
Requires-Python: >=3.11
|
|
23
|
+
Requires-Dist: esda>=2.5
|
|
24
|
+
Requires-Dist: geopandas>=1.0
|
|
25
|
+
Requires-Dist: great-tables>=0.16
|
|
26
|
+
Requires-Dist: inequality>=1.0
|
|
27
|
+
Requires-Dist: libpysal>=4.9
|
|
28
|
+
Requires-Dist: mapclassify>=2.6
|
|
29
|
+
Requires-Dist: mgwr>=2.2
|
|
30
|
+
Requires-Dist: numpy>=1.26
|
|
31
|
+
Requires-Dist: pandas>=2.1
|
|
32
|
+
Requires-Dist: plotly>=5.24
|
|
33
|
+
Requires-Dist: pooch>=1.8
|
|
34
|
+
Requires-Dist: scipy>=1.11
|
|
35
|
+
Requires-Dist: spreg>=1.8
|
|
36
|
+
Requires-Dist: statsmodels>=0.14
|
|
37
|
+
Provides-Extra: all
|
|
38
|
+
Requires-Dist: giddy>=2.3; extra == 'all'
|
|
39
|
+
Requires-Dist: kaleido>=1.0; extra == 'all'
|
|
40
|
+
Provides-Extra: dynamics
|
|
41
|
+
Requires-Dist: giddy>=2.3; extra == 'dynamics'
|
|
42
|
+
Provides-Extra: png
|
|
43
|
+
Requires-Dist: kaleido>=1.0; extra == 'png'
|
|
44
|
+
Description-Content-Type: text/markdown
|
|
45
|
+
|
|
46
|
+
# geometrics
|
|
47
|
+
|
|
48
|
+
[](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml)
|
|
49
|
+
[](https://quarcs-lab.github.io/geometrics/)
|
|
50
|
+
[](https://pypi.org/project/geometrics/)
|
|
51
|
+
[](https://pypi.org/project/geometrics/)
|
|
52
|
+
[](LICENSE)
|
|
53
|
+
[](https://github.com/astral-sh/ruff)
|
|
54
|
+
[](https://colab.research.google.com/github/quarcs-lab/geometrics/blob/main/notebooks/quickstart.ipynb)
|
|
55
|
+
|
|
56
|
+
**geometrics** studies **regional growth, convergence, and inequality** with explicit
|
|
57
|
+
spatial methods. It builds on the excellent [PySAL](https://pysal.org) family —
|
|
58
|
+
[libpysal](https://pysal.org/libpysal/), [esda](https://pysal.org/esda/),
|
|
59
|
+
[giddy](https://pysal.org/giddy/), [inequality](https://pysal.org/inequality/),
|
|
60
|
+
[mapclassify](https://pysal.org/mapclassify/), [spreg](https://pysal.org/spreg/), and
|
|
61
|
+
[mgwr](https://mgwr.readthedocs.io/) — and wraps the standard analyses of the regional
|
|
62
|
+
convergence literature into illustrative, easy-to-apply functions that return
|
|
63
|
+
interactive [Plotly](https://plotly.com/python/) figures,
|
|
64
|
+
[Great Tables](https://posit-dev.github.io/great-tables/), and tidy DataFrames.
|
|
65
|
+
|
|
66
|
+
It follows the design language of [expdpy](https://github.com/cmg777/expdpy): every
|
|
67
|
+
function returns a typed result object with `.df`, `.fig`, plain-language
|
|
68
|
+
`.interpret()`, and concept `.explain()`.
|
|
69
|
+
|
|
70
|
+
## The data model: three inputs
|
|
71
|
+
|
|
72
|
+
| Input | What it is | How it enters |
|
|
73
|
+
|---|---|---|
|
|
74
|
+
| `gdf` | Geometry with **only the entity ID** — shapefile, zipped shapefile, GeoJSON, or GeoPackage (or a GeoDataFrame) | `gm.read_gdf("districts.gpkg", entity="district_id")` |
|
|
75
|
+
| `df` | A **long-form panel** — one row per (entity, time) | `gm.set_panel(df, entity="district_id", time="year")` |
|
|
76
|
+
| `df_dict` | A **data dictionary** — `var_name, var_def, label, type, role, can_be_na` | `gm.set_labels(df, df_dict, set_panel=True)` |
|
|
77
|
+
|
|
78
|
+
## Installation
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
pip install geometrics # core
|
|
82
|
+
pip install "geometrics[dynamics]" # + Markov / spatial Markov (giddy)
|
|
83
|
+
pip install "geometrics[all]" # everything, incl. PNG export
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Quickstart: the Indian case study
|
|
87
|
+
|
|
88
|
+
The bundled case study covers 520 Indian districts observed by satellite nighttime
|
|
89
|
+
lights (1996-2010), from
|
|
90
|
+
[Mendez, Kabiraj & Li (quarcs-lab/project2025s-py)](https://github.com/quarcs-lab/project2025s-py).
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
import geometrics as gm
|
|
94
|
+
|
|
95
|
+
gdf, df, df_dict = gm.data.load_india() # ID-only geometry, long panel, dictionary
|
|
96
|
+
df = gm.set_labels(df, df_dict, set_panel=True)
|
|
97
|
+
|
|
98
|
+
gm.explore_choropleth_map(df, "ntl_total", gdf=gdf, period=2010).fig
|
|
99
|
+
w = gm.make_weights(gdf, method="knn", k=6)
|
|
100
|
+
gm.explore_lisa_cluster_map(df, "log_ntl_pc_1996", gdf=gdf, w=w).fig
|
|
101
|
+
|
|
102
|
+
res = gm.analyze_beta_convergence(
|
|
103
|
+
df, "ntl_total", model="sdm", gdf=gdf, w=w
|
|
104
|
+
)
|
|
105
|
+
print(res.interpret()) # plain-language reading
|
|
106
|
+
res.fig # convergence scatter
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Features
|
|
110
|
+
|
|
111
|
+
- **Maps & ESDA** — classified/animated choropleths (`explore_choropleth_map`), weights
|
|
112
|
+
connectivity (`explore_connectivity_map`), Moran scatterplots, LISA cluster maps,
|
|
113
|
+
Moran over time
|
|
114
|
+
- **Space-time dynamics** — cross-sectional distribution evolution
|
|
115
|
+
(`explore_distribution_over_time`), entity-by-time heatmaps
|
|
116
|
+
- **Convergence** — β-convergence with OLS or spatial (SAR/SEM/SLX/SDM) estimators and
|
|
117
|
+
LeSage-Pace impact decomposition, σ-convergence, Phillips-Sul convergence clubs with
|
|
118
|
+
club maps
|
|
119
|
+
- **Spatial econometrics** — the spreg suite (`analyze_spatial_model`), LM diagnostics
|
|
120
|
+
with a model recommendation (`analyze_spatial_diagnostics`), alternative-weights
|
|
121
|
+
robustness (`analyze_spatial_model_by_weights`)
|
|
122
|
+
- **Distribution dynamics** — Markov and spatial Markov transition analysis
|
|
123
|
+
(`analyze_markov_transitions`, `analyze_spatial_markov`)
|
|
124
|
+
- **Inequality** — Gini/Theil trends with spatial decomposition
|
|
125
|
+
(`analyze_inequality_over_time`), Theil between/within decomposition
|
|
126
|
+
(`analyze_theil_decomposition`)
|
|
127
|
+
- **Local models** — GWR and multiscale GWR with mapped local coefficients
|
|
128
|
+
(`analyze_gwr`, `analyze_mgwr`)
|
|
129
|
+
|
|
130
|
+
## Documentation
|
|
131
|
+
|
|
132
|
+
- Website: https://quarcs-lab.github.io/geometrics/
|
|
133
|
+
- The India case study article and Colab notebooks: see `docs/` and `notebooks/`
|
|
134
|
+
|
|
135
|
+
## Development
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git clone https://github.com/quarcs-lab/geometrics
|
|
139
|
+
cd geometrics
|
|
140
|
+
uv sync --locked --all-extras --group dev --group docs
|
|
141
|
+
make test && make lint && make typecheck
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Citation
|
|
145
|
+
|
|
146
|
+
If you use geometrics in your research, please cite the repository (see
|
|
147
|
+
`CITATION.cff`) and the underlying PySAL packages.
|
|
148
|
+
|
|
149
|
+
## Acknowledgments
|
|
150
|
+
|
|
151
|
+
Developed at the [QuaRCS Lab](https://quarcs-lab.org) (Quantitative Regional and
|
|
152
|
+
Computational Science). geometrics stands on the shoulders of the
|
|
153
|
+
[PySAL](https://pysal.org) project, [geopandas](https://geopandas.org),
|
|
154
|
+
[Plotly](https://plotly.com/python/), and
|
|
155
|
+
[Great Tables](https://posit-dev.github.io/great-tables/).
|
|
156
|
+
|
|
157
|
+
## License
|
|
158
|
+
|
|
159
|
+
MIT
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# geometrics
|
|
2
|
+
|
|
3
|
+
[](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml)
|
|
4
|
+
[](https://quarcs-lab.github.io/geometrics/)
|
|
5
|
+
[](https://pypi.org/project/geometrics/)
|
|
6
|
+
[](https://pypi.org/project/geometrics/)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://github.com/astral-sh/ruff)
|
|
9
|
+
[](https://colab.research.google.com/github/quarcs-lab/geometrics/blob/main/notebooks/quickstart.ipynb)
|
|
10
|
+
|
|
11
|
+
**geometrics** studies **regional growth, convergence, and inequality** with explicit
|
|
12
|
+
spatial methods. It builds on the excellent [PySAL](https://pysal.org) family —
|
|
13
|
+
[libpysal](https://pysal.org/libpysal/), [esda](https://pysal.org/esda/),
|
|
14
|
+
[giddy](https://pysal.org/giddy/), [inequality](https://pysal.org/inequality/),
|
|
15
|
+
[mapclassify](https://pysal.org/mapclassify/), [spreg](https://pysal.org/spreg/), and
|
|
16
|
+
[mgwr](https://mgwr.readthedocs.io/) — and wraps the standard analyses of the regional
|
|
17
|
+
convergence literature into illustrative, easy-to-apply functions that return
|
|
18
|
+
interactive [Plotly](https://plotly.com/python/) figures,
|
|
19
|
+
[Great Tables](https://posit-dev.github.io/great-tables/), and tidy DataFrames.
|
|
20
|
+
|
|
21
|
+
It follows the design language of [expdpy](https://github.com/cmg777/expdpy): every
|
|
22
|
+
function returns a typed result object with `.df`, `.fig`, plain-language
|
|
23
|
+
`.interpret()`, and concept `.explain()`.
|
|
24
|
+
|
|
25
|
+
## The data model: three inputs
|
|
26
|
+
|
|
27
|
+
| Input | What it is | How it enters |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| `gdf` | Geometry with **only the entity ID** — shapefile, zipped shapefile, GeoJSON, or GeoPackage (or a GeoDataFrame) | `gm.read_gdf("districts.gpkg", entity="district_id")` |
|
|
30
|
+
| `df` | A **long-form panel** — one row per (entity, time) | `gm.set_panel(df, entity="district_id", time="year")` |
|
|
31
|
+
| `df_dict` | A **data dictionary** — `var_name, var_def, label, type, role, can_be_na` | `gm.set_labels(df, df_dict, set_panel=True)` |
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
pip install geometrics # core
|
|
37
|
+
pip install "geometrics[dynamics]" # + Markov / spatial Markov (giddy)
|
|
38
|
+
pip install "geometrics[all]" # everything, incl. PNG export
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Quickstart: the Indian case study
|
|
42
|
+
|
|
43
|
+
The bundled case study covers 520 Indian districts observed by satellite nighttime
|
|
44
|
+
lights (1996-2010), from
|
|
45
|
+
[Mendez, Kabiraj & Li (quarcs-lab/project2025s-py)](https://github.com/quarcs-lab/project2025s-py).
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import geometrics as gm
|
|
49
|
+
|
|
50
|
+
gdf, df, df_dict = gm.data.load_india() # ID-only geometry, long panel, dictionary
|
|
51
|
+
df = gm.set_labels(df, df_dict, set_panel=True)
|
|
52
|
+
|
|
53
|
+
gm.explore_choropleth_map(df, "ntl_total", gdf=gdf, period=2010).fig
|
|
54
|
+
w = gm.make_weights(gdf, method="knn", k=6)
|
|
55
|
+
gm.explore_lisa_cluster_map(df, "log_ntl_pc_1996", gdf=gdf, w=w).fig
|
|
56
|
+
|
|
57
|
+
res = gm.analyze_beta_convergence(
|
|
58
|
+
df, "ntl_total", model="sdm", gdf=gdf, w=w
|
|
59
|
+
)
|
|
60
|
+
print(res.interpret()) # plain-language reading
|
|
61
|
+
res.fig # convergence scatter
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Features
|
|
65
|
+
|
|
66
|
+
- **Maps & ESDA** — classified/animated choropleths (`explore_choropleth_map`), weights
|
|
67
|
+
connectivity (`explore_connectivity_map`), Moran scatterplots, LISA cluster maps,
|
|
68
|
+
Moran over time
|
|
69
|
+
- **Space-time dynamics** — cross-sectional distribution evolution
|
|
70
|
+
(`explore_distribution_over_time`), entity-by-time heatmaps
|
|
71
|
+
- **Convergence** — β-convergence with OLS or spatial (SAR/SEM/SLX/SDM) estimators and
|
|
72
|
+
LeSage-Pace impact decomposition, σ-convergence, Phillips-Sul convergence clubs with
|
|
73
|
+
club maps
|
|
74
|
+
- **Spatial econometrics** — the spreg suite (`analyze_spatial_model`), LM diagnostics
|
|
75
|
+
with a model recommendation (`analyze_spatial_diagnostics`), alternative-weights
|
|
76
|
+
robustness (`analyze_spatial_model_by_weights`)
|
|
77
|
+
- **Distribution dynamics** — Markov and spatial Markov transition analysis
|
|
78
|
+
(`analyze_markov_transitions`, `analyze_spatial_markov`)
|
|
79
|
+
- **Inequality** — Gini/Theil trends with spatial decomposition
|
|
80
|
+
(`analyze_inequality_over_time`), Theil between/within decomposition
|
|
81
|
+
(`analyze_theil_decomposition`)
|
|
82
|
+
- **Local models** — GWR and multiscale GWR with mapped local coefficients
|
|
83
|
+
(`analyze_gwr`, `analyze_mgwr`)
|
|
84
|
+
|
|
85
|
+
## Documentation
|
|
86
|
+
|
|
87
|
+
- Website: https://quarcs-lab.github.io/geometrics/
|
|
88
|
+
- The India case study article and Colab notebooks: see `docs/` and `notebooks/`
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git clone https://github.com/quarcs-lab/geometrics
|
|
94
|
+
cd geometrics
|
|
95
|
+
uv sync --locked --all-extras --group dev --group docs
|
|
96
|
+
make test && make lint && make typecheck
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Citation
|
|
100
|
+
|
|
101
|
+
If you use geometrics in your research, please cite the repository (see
|
|
102
|
+
`CITATION.cff`) and the underlying PySAL packages.
|
|
103
|
+
|
|
104
|
+
## Acknowledgments
|
|
105
|
+
|
|
106
|
+
Developed at the [QuaRCS Lab](https://quarcs-lab.org) (Quantitative Regional and
|
|
107
|
+
Computational Science). geometrics stands on the shoulders of the
|
|
108
|
+
[PySAL](https://pysal.org) project, [geopandas](https://geopandas.org),
|
|
109
|
+
[Plotly](https://plotly.com/python/), and
|
|
110
|
+
[Great Tables](https://posit-dev.github.io/great-tables/).
|
|
111
|
+
|
|
112
|
+
## License
|
|
113
|
+
|
|
114
|
+
MIT
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "geometrics"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Regional growth, convergence, and inequality analysis on the PySAL stack."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.11"
|
|
12
|
+
authors = [{ name = "Carlos Mendez" }]
|
|
13
|
+
keywords = [
|
|
14
|
+
"spatial econometrics",
|
|
15
|
+
"regional science",
|
|
16
|
+
"convergence",
|
|
17
|
+
"inequality",
|
|
18
|
+
"pysal",
|
|
19
|
+
"geopandas",
|
|
20
|
+
"spatial analysis",
|
|
21
|
+
"plotly",
|
|
22
|
+
]
|
|
23
|
+
classifiers = [
|
|
24
|
+
"Development Status :: 3 - Alpha",
|
|
25
|
+
"Intended Audience :: Science/Research",
|
|
26
|
+
"License :: OSI Approved :: MIT License",
|
|
27
|
+
"Programming Language :: Python :: 3",
|
|
28
|
+
"Programming Language :: Python :: 3.11",
|
|
29
|
+
"Programming Language :: Python :: 3.12",
|
|
30
|
+
"Programming Language :: Python :: 3.13",
|
|
31
|
+
"Topic :: Scientific/Engineering",
|
|
32
|
+
"Topic :: Scientific/Engineering :: GIS",
|
|
33
|
+
]
|
|
34
|
+
dependencies = [
|
|
35
|
+
"pandas>=2.1",
|
|
36
|
+
"numpy>=1.26",
|
|
37
|
+
"scipy>=1.11",
|
|
38
|
+
"geopandas>=1.0",
|
|
39
|
+
"libpysal>=4.9",
|
|
40
|
+
"esda>=2.5",
|
|
41
|
+
"inequality>=1.0",
|
|
42
|
+
"mapclassify>=2.6",
|
|
43
|
+
"spreg>=1.8",
|
|
44
|
+
"mgwr>=2.2",
|
|
45
|
+
"statsmodels>=0.14",
|
|
46
|
+
"plotly>=5.24",
|
|
47
|
+
"great-tables>=0.16",
|
|
48
|
+
"pooch>=1.8",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
[project.optional-dependencies]
|
|
52
|
+
# Distribution dynamics (Markov / Spatial Markov) via giddy. Kept out of core because the
|
|
53
|
+
# giddy -> quantecon -> numba chain has no recent wheels on some platforms (macOS x86_64);
|
|
54
|
+
# core `pip install geometrics` must never fall back to an sdist build.
|
|
55
|
+
dynamics = [
|
|
56
|
+
"giddy>=2.3",
|
|
57
|
+
]
|
|
58
|
+
# Static PNG export of Plotly figures (tile-free maps export deterministically; tile maps
|
|
59
|
+
# need kaleido>=1.0's Chromium engine).
|
|
60
|
+
png = [
|
|
61
|
+
"kaleido>=1.0",
|
|
62
|
+
]
|
|
63
|
+
all = [
|
|
64
|
+
"geometrics[dynamics,png]",
|
|
65
|
+
]
|
|
66
|
+
|
|
67
|
+
[project.urls]
|
|
68
|
+
Homepage = "https://github.com/quarcs-lab/geometrics"
|
|
69
|
+
Documentation = "https://quarcs-lab.github.io/geometrics/"
|
|
70
|
+
Repository = "https://github.com/quarcs-lab/geometrics"
|
|
71
|
+
Issues = "https://github.com/quarcs-lab/geometrics/issues"
|
|
72
|
+
|
|
73
|
+
[dependency-groups]
|
|
74
|
+
dev = [
|
|
75
|
+
"pytest>=8",
|
|
76
|
+
"pytest-cov>=5",
|
|
77
|
+
"pytest-xdist>=3",
|
|
78
|
+
"hypothesis>=6",
|
|
79
|
+
"ruff>=0.6",
|
|
80
|
+
"mypy>=1.10",
|
|
81
|
+
"pandas-stubs",
|
|
82
|
+
"pre-commit>=3.7",
|
|
83
|
+
]
|
|
84
|
+
docs = [
|
|
85
|
+
"quartodoc>=0.11",
|
|
86
|
+
"griffe<1",
|
|
87
|
+
"jupyter",
|
|
88
|
+
"nbformat>=5.9",
|
|
89
|
+
]
|
|
90
|
+
|
|
91
|
+
[tool.uv]
|
|
92
|
+
# giddy -> quantecon -> numba: without a floor the resolver escapes numba's numpy cap
|
|
93
|
+
# by walking down to ancient sdist-only releases (numba 0.53 / llvmlite 0.36) that
|
|
94
|
+
# cannot build on Python 3.11+. The global floor forces a wheel-bearing numba and lets
|
|
95
|
+
# numpy resolve downward instead. On Intel macs the last numba with wheels is 0.60,
|
|
96
|
+
# which caps numpy at 2.0.x there.
|
|
97
|
+
constraint-dependencies = [
|
|
98
|
+
"numba>=0.60",
|
|
99
|
+
"numba<0.61; sys_platform == 'darwin' and platform_machine == 'x86_64'",
|
|
100
|
+
"numpy<2.1; sys_platform == 'darwin' and platform_machine == 'x86_64'",
|
|
101
|
+
# Without these floors the resolver escapes numba's numpy cap by selecting an
|
|
102
|
+
# ancient quantecon (0.3.x declares no numba dependency and breaks at import).
|
|
103
|
+
"quantecon>=0.7",
|
|
104
|
+
"giddy>=2.3.5",
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
[tool.hatch.build.targets.wheel]
|
|
108
|
+
packages = ["src/geometrics"]
|
|
109
|
+
|
|
110
|
+
[tool.hatch.build.targets.sdist]
|
|
111
|
+
include = [
|
|
112
|
+
"src/geometrics",
|
|
113
|
+
"tests",
|
|
114
|
+
"README.md",
|
|
115
|
+
"LICENSE",
|
|
116
|
+
]
|
|
117
|
+
|
|
118
|
+
# ----------------------------------------------------------------------------
|
|
119
|
+
# Tooling
|
|
120
|
+
# ----------------------------------------------------------------------------
|
|
121
|
+
[tool.ruff]
|
|
122
|
+
line-length = 88
|
|
123
|
+
target-version = "py311"
|
|
124
|
+
src = ["src", "tests"]
|
|
125
|
+
|
|
126
|
+
[tool.ruff.lint]
|
|
127
|
+
select = ["E", "F", "W", "I", "UP", "B", "SIM", "D", "RUF"]
|
|
128
|
+
ignore = [
|
|
129
|
+
"D100", # missing docstring in public module
|
|
130
|
+
"D104", # missing docstring in public package
|
|
131
|
+
"D105", # missing docstring in magic method
|
|
132
|
+
"D107", # missing docstring in __init__
|
|
133
|
+
"E501", # line length handled by formatter
|
|
134
|
+
"B008", # function call in default argument (intentional for np.nanmean etc.)
|
|
135
|
+
"RUF022", # __all__ is intentionally grouped by topic, not sorted
|
|
136
|
+
]
|
|
137
|
+
# Greek symbols used in scientific prose/labels (β and λ are not flagged; σ is confusable
|
|
138
|
+
# with o, so it must be allowed explicitly to read as "σ-convergence" alongside
|
|
139
|
+
# "β-convergence"; ρ is the spatial autoregressive parameter, γ the SLX lag coefficients,
|
|
140
|
+
# α the significance level).
|
|
141
|
+
allowed-confusables = ["σ", "ρ", "γ", "α"]
|
|
142
|
+
|
|
143
|
+
[tool.ruff.lint.pydocstyle]
|
|
144
|
+
convention = "numpy"
|
|
145
|
+
|
|
146
|
+
[tool.ruff.lint.per-file-ignores]
|
|
147
|
+
"tests/*" = ["D"]
|
|
148
|
+
"docs/*" = ["D"]
|
|
149
|
+
"tools/*" = ["D"]
|
|
150
|
+
|
|
151
|
+
[tool.mypy]
|
|
152
|
+
# 3.12, not the 3.11 package floor: numpy >= 2.5 ships PEP 695 `type` statements in its
|
|
153
|
+
# stubs, which mypy only parses at target 3.12+. Runtime 3.11 compatibility is covered
|
|
154
|
+
# by ruff's py311 target and the 3.11 cells of the CI test matrix.
|
|
155
|
+
python_version = "3.12"
|
|
156
|
+
warn_unused_configs = true
|
|
157
|
+
ignore_missing_imports = true
|
|
158
|
+
files = ["src/geometrics"]
|
|
159
|
+
|
|
160
|
+
[tool.pytest.ini_options]
|
|
161
|
+
minversion = "8.0"
|
|
162
|
+
addopts = "--strict-markers --strict-config"
|
|
163
|
+
testpaths = ["tests"]
|
|
164
|
+
markers = [
|
|
165
|
+
"network: tests that download the real case-study data from GitHub",
|
|
166
|
+
"dynamics: tests that require the optional giddy (distribution dynamics) extra",
|
|
167
|
+
"slow: long-running tests (GWR on the full case study, large Monte-Carlo draws)",
|
|
168
|
+
]
|
|
169
|
+
filterwarnings = ["ignore::DeprecationWarning"]
|
|
170
|
+
|
|
171
|
+
[tool.coverage.run]
|
|
172
|
+
source = ["geometrics"]
|
|
173
|
+
branch = true
|
|
174
|
+
|
|
175
|
+
[tool.coverage.report]
|
|
176
|
+
show_missing = true
|
|
177
|
+
exclude_lines = ["pragma: no cover", "raise NotImplementedError", "if TYPE_CHECKING:"]
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""geometrics: regional growth, convergence, and inequality on the PySAL stack.
|
|
2
|
+
|
|
3
|
+
geometrics wraps the standard analyses of the regional convergence literature —
|
|
4
|
+
exploratory spatial data analysis, β/σ/club convergence, spatial econometric models,
|
|
5
|
+
distribution dynamics, inequality decomposition, and local (GWR) models — into
|
|
6
|
+
illustrative, easy-to-apply functions built on libpysal, esda, giddy, inequality,
|
|
7
|
+
mapclassify, spreg, and mgwr.
|
|
8
|
+
|
|
9
|
+
Three inputs drive everything: a geometry with only the entity ID (``read_gdf``),
|
|
10
|
+
a long-form panel (``set_panel`` / ``set_labels``), and a data dictionary
|
|
11
|
+
(``df_dict``, inferable with ``build_data_dict``). Every public function returns a
|
|
12
|
+
frozen result dataclass with ``.df``, ``.fig`` and/or ``.gt``, plain-language
|
|
13
|
+
``.interpret()``, and a concept ``.explain()``.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from geometrics import data
|
|
17
|
+
from geometrics._data_dict import build_data_dict
|
|
18
|
+
from geometrics._geo import read_gdf
|
|
19
|
+
from geometrics._labels import resolve_label, set_labels
|
|
20
|
+
from geometrics._panel import resolve_panel, set_panel
|
|
21
|
+
from geometrics._roles import set_roles
|
|
22
|
+
from geometrics._theme import get_palette, set_palette
|
|
23
|
+
from geometrics._types import (
|
|
24
|
+
BetaConvergenceResult,
|
|
25
|
+
ChoroplethMapResult,
|
|
26
|
+
ConnectivityMapResult,
|
|
27
|
+
ConvergenceClubsResult,
|
|
28
|
+
DistributionOverTimeResult,
|
|
29
|
+
GWRResult,
|
|
30
|
+
InequalityOverTimeResult,
|
|
31
|
+
LisaClusterMapResult,
|
|
32
|
+
MarkovTransitionsResult,
|
|
33
|
+
MGWRResult,
|
|
34
|
+
MoranOverTimeResult,
|
|
35
|
+
MoranPlotResult,
|
|
36
|
+
SigmaConvergenceResult,
|
|
37
|
+
SpacetimeHeatmapResult,
|
|
38
|
+
SpatialDiagnosticsResult,
|
|
39
|
+
SpatialMarkovResult,
|
|
40
|
+
SpatialModelResult,
|
|
41
|
+
TheilDecompositionResult,
|
|
42
|
+
WeightsRobustnessResult,
|
|
43
|
+
)
|
|
44
|
+
from geometrics.clubs import analyze_convergence_clubs
|
|
45
|
+
from geometrics.convergence import (
|
|
46
|
+
analyze_beta_convergence,
|
|
47
|
+
analyze_sigma_convergence,
|
|
48
|
+
growth_cross_section,
|
|
49
|
+
)
|
|
50
|
+
from geometrics.dependence import (
|
|
51
|
+
explore_lisa_cluster_map,
|
|
52
|
+
explore_moran_over_time,
|
|
53
|
+
explore_moran_plot,
|
|
54
|
+
)
|
|
55
|
+
from geometrics.distribution_dynamics import (
|
|
56
|
+
analyze_markov_transitions,
|
|
57
|
+
analyze_spatial_markov,
|
|
58
|
+
)
|
|
59
|
+
from geometrics.gwr import analyze_gwr, analyze_mgwr
|
|
60
|
+
from geometrics.maps import explore_choropleth_map
|
|
61
|
+
from geometrics.pedagogy import Explainer, explain, list_topics
|
|
62
|
+
from geometrics.regional_inequality import (
|
|
63
|
+
analyze_inequality_over_time,
|
|
64
|
+
analyze_theil_decomposition,
|
|
65
|
+
)
|
|
66
|
+
from geometrics.spacetime import (
|
|
67
|
+
explore_distribution_over_time,
|
|
68
|
+
explore_spacetime_heatmap,
|
|
69
|
+
)
|
|
70
|
+
from geometrics.spatial_models import (
|
|
71
|
+
analyze_spatial_diagnostics,
|
|
72
|
+
analyze_spatial_model,
|
|
73
|
+
analyze_spatial_model_by_weights,
|
|
74
|
+
)
|
|
75
|
+
from geometrics.weights import explore_connectivity_map, make_weights
|
|
76
|
+
|
|
77
|
+
__version__ = "0.1.0"
|
|
78
|
+
|
|
79
|
+
__all__ = [
|
|
80
|
+
# ===== EXPLORE =====
|
|
81
|
+
# maps
|
|
82
|
+
"explore_choropleth_map",
|
|
83
|
+
# spatial weights
|
|
84
|
+
"explore_connectivity_map",
|
|
85
|
+
# spatial dependence (ESDA)
|
|
86
|
+
"explore_moran_plot",
|
|
87
|
+
"explore_lisa_cluster_map",
|
|
88
|
+
"explore_moran_over_time",
|
|
89
|
+
# space-time dynamics
|
|
90
|
+
"explore_distribution_over_time",
|
|
91
|
+
"explore_spacetime_heatmap",
|
|
92
|
+
# ===== ANALYZE =====
|
|
93
|
+
# convergence
|
|
94
|
+
"analyze_beta_convergence",
|
|
95
|
+
"analyze_sigma_convergence",
|
|
96
|
+
"analyze_convergence_clubs",
|
|
97
|
+
# spatial econometric models (spreg)
|
|
98
|
+
"analyze_spatial_model",
|
|
99
|
+
"analyze_spatial_diagnostics",
|
|
100
|
+
"analyze_spatial_model_by_weights",
|
|
101
|
+
# distribution dynamics (giddy)
|
|
102
|
+
"analyze_markov_transitions",
|
|
103
|
+
"analyze_spatial_markov",
|
|
104
|
+
# regional inequality (PySAL inequality)
|
|
105
|
+
"analyze_inequality_over_time",
|
|
106
|
+
"analyze_theil_decomposition",
|
|
107
|
+
# local models (mgwr)
|
|
108
|
+
"analyze_gwr",
|
|
109
|
+
"analyze_mgwr",
|
|
110
|
+
# ===== UTILITIES =====
|
|
111
|
+
"read_gdf",
|
|
112
|
+
"make_weights",
|
|
113
|
+
"growth_cross_section",
|
|
114
|
+
"set_panel",
|
|
115
|
+
"resolve_panel",
|
|
116
|
+
"set_labels",
|
|
117
|
+
"resolve_label",
|
|
118
|
+
"set_roles",
|
|
119
|
+
"build_data_dict",
|
|
120
|
+
"set_palette",
|
|
121
|
+
"get_palette",
|
|
122
|
+
"explain",
|
|
123
|
+
"list_topics",
|
|
124
|
+
"Explainer",
|
|
125
|
+
# ===== DATA =====
|
|
126
|
+
"data",
|
|
127
|
+
# ===== RESULT TYPES =====
|
|
128
|
+
"ChoroplethMapResult",
|
|
129
|
+
"ConnectivityMapResult",
|
|
130
|
+
"MoranPlotResult",
|
|
131
|
+
"LisaClusterMapResult",
|
|
132
|
+
"MoranOverTimeResult",
|
|
133
|
+
"DistributionOverTimeResult",
|
|
134
|
+
"SpacetimeHeatmapResult",
|
|
135
|
+
"BetaConvergenceResult",
|
|
136
|
+
"SigmaConvergenceResult",
|
|
137
|
+
"ConvergenceClubsResult",
|
|
138
|
+
"SpatialModelResult",
|
|
139
|
+
"SpatialDiagnosticsResult",
|
|
140
|
+
"WeightsRobustnessResult",
|
|
141
|
+
"MarkovTransitionsResult",
|
|
142
|
+
"SpatialMarkovResult",
|
|
143
|
+
"InequalityOverTimeResult",
|
|
144
|
+
"TheilDecompositionResult",
|
|
145
|
+
"GWRResult",
|
|
146
|
+
"MGWRResult",
|
|
147
|
+
]
|