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.
Files changed (79) hide show
  1. geometrics-0.1.0/.gitignore +29 -0
  2. geometrics-0.1.0/LICENSE +21 -0
  3. geometrics-0.1.0/PKG-INFO +159 -0
  4. geometrics-0.1.0/README.md +114 -0
  5. geometrics-0.1.0/pyproject.toml +177 -0
  6. geometrics-0.1.0/src/geometrics/__init__.py +147 -0
  7. geometrics-0.1.0/src/geometrics/_common.py +193 -0
  8. geometrics-0.1.0/src/geometrics/_data_dict.py +369 -0
  9. geometrics-0.1.0/src/geometrics/_geo.py +641 -0
  10. geometrics-0.1.0/src/geometrics/_impacts.py +580 -0
  11. geometrics-0.1.0/src/geometrics/_labels.py +219 -0
  12. geometrics-0.1.0/src/geometrics/_mapping.py +641 -0
  13. geometrics-0.1.0/src/geometrics/_panel.py +179 -0
  14. geometrics-0.1.0/src/geometrics/_roles.py +143 -0
  15. geometrics-0.1.0/src/geometrics/_theme.py +410 -0
  16. geometrics-0.1.0/src/geometrics/_types.py +939 -0
  17. geometrics-0.1.0/src/geometrics/_validation.py +192 -0
  18. geometrics-0.1.0/src/geometrics/clubs.py +1044 -0
  19. geometrics-0.1.0/src/geometrics/convergence.py +1545 -0
  20. geometrics-0.1.0/src/geometrics/data/__init__.py +262 -0
  21. geometrics-0.1.0/src/geometrics/data/_registry.py +82 -0
  22. geometrics-0.1.0/src/geometrics/data/india32_dict.csv +7 -0
  23. geometrics-0.1.0/src/geometrics/data/india520_dict.csv +29 -0
  24. geometrics-0.1.0/src/geometrics/dependence.py +926 -0
  25. geometrics-0.1.0/src/geometrics/distribution_dynamics.py +858 -0
  26. geometrics-0.1.0/src/geometrics/gwr.py +909 -0
  27. geometrics-0.1.0/src/geometrics/maps.py +491 -0
  28. geometrics-0.1.0/src/geometrics/pedagogy/__init__.py +32 -0
  29. geometrics-0.1.0/src/geometrics/pedagogy/_format.py +88 -0
  30. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/__init__.py +63 -0
  31. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_convergence.py +264 -0
  32. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_dependence.py +207 -0
  33. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_dynamics.py +211 -0
  34. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_gwr.py +161 -0
  35. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_inequality.py +226 -0
  36. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_maps.py +70 -0
  37. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_shared.py +16 -0
  38. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_spacetime.py +201 -0
  39. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_spatial_models.py +319 -0
  40. geometrics-0.1.0/src/geometrics/pedagogy/_interpret/_weights.py +86 -0
  41. geometrics-0.1.0/src/geometrics/pedagogy/_mixin.py +44 -0
  42. geometrics-0.1.0/src/geometrics/pedagogy/_registry.py +124 -0
  43. geometrics-0.1.0/src/geometrics/pedagogy/_text/__init__.py +25 -0
  44. geometrics-0.1.0/src/geometrics/pedagogy/_text/convergence.py +201 -0
  45. geometrics-0.1.0/src/geometrics/pedagogy/_text/correlation.py +77 -0
  46. geometrics-0.1.0/src/geometrics/pedagogy/_text/dynamics.py +157 -0
  47. geometrics-0.1.0/src/geometrics/pedagogy/_text/inequality.py +124 -0
  48. geometrics-0.1.0/src/geometrics/pedagogy/_text/models.py +326 -0
  49. geometrics-0.1.0/src/geometrics/pedagogy/_text/spatial.py +263 -0
  50. geometrics-0.1.0/src/geometrics/py.typed +0 -0
  51. geometrics-0.1.0/src/geometrics/regional_inequality.py +985 -0
  52. geometrics-0.1.0/src/geometrics/spacetime.py +665 -0
  53. geometrics-0.1.0/src/geometrics/spatial_models.py +1349 -0
  54. geometrics-0.1.0/src/geometrics/weights.py +578 -0
  55. geometrics-0.1.0/tests/conftest.py +109 -0
  56. geometrics-0.1.0/tests/fixtures/make_fixtures.py +98 -0
  57. geometrics-0.1.0/tests/fixtures/mini520.dta +0 -0
  58. geometrics-0.1.0/tests/fixtures/mini520.geojson +13 -0
  59. geometrics-0.1.0/tests/test_clubs.py +306 -0
  60. geometrics-0.1.0/tests/test_convergence.py +458 -0
  61. geometrics-0.1.0/tests/test_data.py +268 -0
  62. geometrics-0.1.0/tests/test_data_dict.py +125 -0
  63. geometrics-0.1.0/tests/test_dependence.py +451 -0
  64. geometrics-0.1.0/tests/test_dynamics.py +345 -0
  65. geometrics-0.1.0/tests/test_geo.py +433 -0
  66. geometrics-0.1.0/tests/test_gwr.py +321 -0
  67. geometrics-0.1.0/tests/test_impacts.py +439 -0
  68. geometrics-0.1.0/tests/test_inequality.py +395 -0
  69. geometrics-0.1.0/tests/test_labels.py +80 -0
  70. geometrics-0.1.0/tests/test_maps.py +422 -0
  71. geometrics-0.1.0/tests/test_panel.py +59 -0
  72. geometrics-0.1.0/tests/test_paper_parity.py +134 -0
  73. geometrics-0.1.0/tests/test_pedagogy.py +68 -0
  74. geometrics-0.1.0/tests/test_roles.py +170 -0
  75. geometrics-0.1.0/tests/test_spacetime.py +368 -0
  76. geometrics-0.1.0/tests/test_spatial_models.py +445 -0
  77. geometrics-0.1.0/tests/test_theme.py +113 -0
  78. geometrics-0.1.0/tests/test_validation.py +95 -0
  79. 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
@@ -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
+ [![CI](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml/badge.svg)](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml)
49
+ [![Docs](https://img.shields.io/badge/docs-quarcs--lab.github.io%2Fgeometrics-blue)](https://quarcs-lab.github.io/geometrics/)
50
+ [![PyPI](https://img.shields.io/pypi/v/geometrics.svg)](https://pypi.org/project/geometrics/)
51
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/geometrics/)
52
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
53
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
54
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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
+ [![CI](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml/badge.svg)](https://github.com/quarcs-lab/geometrics/actions/workflows/ci.yml)
4
+ [![Docs](https://img.shields.io/badge/docs-quarcs--lab.github.io%2Fgeometrics-blue)](https://quarcs-lab.github.io/geometrics/)
5
+ [![PyPI](https://img.shields.io/pypi/v/geometrics.svg)](https://pypi.org/project/geometrics/)
6
+ [![Python](https://img.shields.io/badge/python-3.11%2B-blue)](https://pypi.org/project/geometrics/)
7
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
8
+ [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
9
+ [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](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
+ ]