Introduction to moveq
moveq is a fast, modular, and mathematically auditable Python toolkit for transport equity analysis, spatial justice auditing, and cross-country policy benchmarking.
It transforms raw municipal transit metrics (e.g. trips per neighborhood, morning peak frequency, evening coverage buffers) paired with spatial population arrays into population-weighted inequality coefficients, resilient composite scores, and validated cross-country questionnaire catalogues.
moveq guarantees deterministic invariance against spatial zone reordering and tied rank deciles.
Design Philosophy
Unlike monolithic GIS toolboxes that require heavy C libraries (GDAL, GEOS, PROJ) or opaque spreadsheet formulas that fail silently when demographic columns are missing, moveq was built from first principles with four non-negotiable architectural invariants:
-
1. Pure NumPy Zero-Bloat Core: The primary mathematical engine (
moveq-core) depends strictly on NumPy. It imports in under 5 milliseconds and runs identically on laptops, embedded IoT routers, microservices, AWS Lambda, and browser WebAssembly. -
2. Continuous Boundary Slicing: Standard quantile binning produces erratic jumps when demographic zones cross the 40% or 90% population thresholds.
moveqcalculates exact continuous proportional splitting across boundary units. -
3. Graceful Renormalization Over Silent Padding: If an indicator (e.g., weekend night frequency) is missing from a dataset cut,
moveqdoes NOT pad with zeros (which falsely penalizes scores). Instead, it drops the missing term and dynamically renormalizes the remaining design weights, generating an explicit note in the audit trail. -
4. Strict Harmonization Contracts: When comparing transit equity across borders (e.g., UK LSOAs vs. French IRIS zones vs. US Census Tracts),
moveq-cataloguestrictly validates whether each section issame,replace(with new title), oromit(with justification note).
Installation Options
moveq is packaged into modular distributions so you only install what you need:
# 1. Full Stack: Core math + Composite Scoring + Catalogue + CLI + Pandas DataFrame helpers
pip install "moveq[cli,frames]"
# 2. Lightning-fast UV package manager
uv add "moveq[cli,frames]"
# 3. Pure NumPy Core Only (zero GIS/Pandas dependencies, < 50KB package size)
pip install moveq-core
# 4. Conda Forge Distribution
conda install -c conda-forge moveq
Quickstart Tutorial
Here is a complete, runnable example computing the Gini coefficient, Palma ratio, Wagstaff Concentration Index, and composite accessibility score:
import numpy as np
from moveq import (
compute_gini,
compute_palma_ratio,
compute_concentration_index,
compute_score,
)
# 1. Define municipal data for 5 spatial zones (e.g. Census Tracts or LSOAs)
trips_per_capita = np.array([10.0, 20.0, 5.0, 50.0, 8.0])
population = np.array([1000, 800, 1200, 300, 900])
deprivation_rank = np.array([1, 3, 2, 5, 4]) # 1 = most deprived neighborhood
# 2. Population-Weighted Gini Coefficient (Overall Inequality)
gini = compute_gini(trips_per_capita, population)
print(f"Gini Coefficient: {gini:.4f}")
# Output: Gini Coefficient: 0.3902
# 3. Continuous Boundary Palma Ratio (Top 10% vs Bottom 40%)
palma = compute_palma_ratio(trips_per_capita, population)
print(f"Palma Ratio: {palma:.4f}")
# Output: Palma Ratio: 7.0732
# 4. Wagstaff Concentration Index (Pro-Poor vs Pro-Rich Disparity)
ci = compute_concentration_index(trips_per_capita, deprivation_rank, population)
print(f"Concentration Index: {ci:.4f}")
# Output: Concentration Index: 0.2457 (> 0 indicates service favors less deprived areas)
# 5. Composite Accessibility Score (with missing term dropped)
score_result = compute_score(
terms={
"buffer_400m": 0.85, # 85% coverage
"evening_freq": 0.60, # 60% standard
"night_service": None, # Data not collected in this city
},
weights={
"buffer_400m": 0.50,
"evening_freq": 0.30,
"night_service": 0.20,
},
labels={
"buffer_400m": "400m Transit Stop Access",
"evening_freq": "Evening Service Frequency",
"night_service": "24-Hour Night Transit",
}
)
print(f"Composite Score: {score_result.score:.1f} / 100")
print(f"Audit Trail Note: {score_result.note}")
# Output:
# Composite Score: 75.6 / 100
# Audit Trail Note: 24-hour night transit not in this cut — weights renormalised.
Architecture & Module Matrix
The library is split into 4 decoupled subpackages under the moveq umbrella:
| Package | Primary Imports | Purpose & Core Invariants |
|---|---|---|
| moveq-core | moveq_core.equitymoveq_core.score |
Pure NumPy engine for Gini, continuous Palma, Concentration Index, and composite scoring with dynamic weight renormalization. |
| moveq-catalogue | moveq_catalogue.catalogue |
Harmonization registry enforcing explicit same / replace / omit questionnaire mapping for cross-country studies. |
| moveq | import moveq |
Convenience top-level re-export package providing clean imports across all modules. |
| moveq-cli | moveq command |
Standalone command-line interface for running equity audits on CSV files and JSON payloads in headless CI/CD pipelines. |