moveq-core
Pure NumPy algorithms for measuring transport equity.
This is the calculation engine of the moveq
stack. It has no I/O: you pass NumPy arrays (or, with the optional
frames extra, a pandas DataFrame) and get numbers and small result objects
back. There is no database, GIS library, or network client.
Most applications should install the umbrella package
moveq, which re-exports this API.
Install moveq-core directly only when you want the metrics without the
catalogue registry.
What each metric is for
Transport analysis usually has a service variable per area (trips, departures, coverage) and a population weight. Sometimes there is also a socioeconomic rank (deprivation index, income band).
Population-weighted Gini
Gini answers: how unevenly is this service spread across people?
Areas are sorted from lowest to highest service. Cumulative population share
is plotted against cumulative service share (a Lorenz curve). Gini is 1
minus twice the area under that curve, using trapezoidal integration.
0— every person faces the same service level (including the all-zero convention: nothing to distribute)- toward
1— almost all service sits with a small share of the population
Gini does not know whether the poorly served people are deprived. It is a statement about the service distribution only.
Palma ratio
Palma answers: how does the best-served tenth compare with the worst-served two-fifths?
It is the population-weighted mean service of the top 10% divided by that of
the bottom 40% (areas on the boundary of those shares are split
proportionally). Equal service, including all-zero service, gives 1. If
the bottom 40% have zero service while the top 10% do not, the function
returns inf rather than dividing by zero.
Palma is more sensitive to the tails than Gini, which is why the two are reported together.
Wagstaff Concentration Index
The concentration index answers: does service rise or fall as deprivation falls?
Areas are ordered by rank (1 = most deprived by convention in this
library). Units that tie on rank share the group's midpoint fractional
rank, so the answer does not depend on input order. Unpopulated units are
ignored. A population-weighted covariance between service and fractional
rank is scaled by the mean service. For non-negative service the result is
in [-1, 1]:
| Sign | Meaning |
|---|---|
CI > 0 |
outcome concentrated among more-advantaged observations (advantage-concentrated) |
CI < 0 |
outcome concentrated among less-advantaged observations (disadvantage-concentrated) |
CI = 0 |
no systematic gradient with rank |
This is the metric to use when the policy question is about socioeconomic targeting, not only about overall unevenness.
Composite score with missing-term renormalisation
compute_score builds a 0–100 weighted index from named terms in [0, 1].
The important behaviour is what happens when a term is None:
- that term is dropped, not treated as zero
- remaining design weights are renormalised to sum to 1
- the result records
weight_usedvsdesign_weight, the dropped ids, and anote
If every term is missing, score is None. Design weights must be finite
and strictly positive; non-finite term values are rejected rather than
clipped. Callers keep control of which indicators exist; the library
refuses to invent a zero.
Optional DataFrame helpers (moveq_core.frames)
With pip install "moveq-core[frames]" (or "moveq[frames]"):
- vulnerability index — min-max scale several deprivation-like columns to 0–100 and take the row-wise mean
- multiply deprived — flag areas that sit in the worst tertile on at least k factors at once
These are convenience summaries, not a substitute for a national IMD.
Formulae: methodology.
Installation
Requires Python 3.10+. Runtime dependency: numpy>=1.24.
pip install moveq-core
pip install "moveq-core[frames]" # pandas helpers
Quickstart
import numpy as np
from moveq_core import (
compute_gini,
compute_palma_ratio,
compute_concentration_index,
gini_result,
compute_score,
)
service = 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
gini = compute_gini(service, population)
palma = compute_palma_ratio(service, population)
ci = compute_concentration_index(
service, deprivation_rank, population, rank_direction="higher_is_advantaged"
)
gini_audit = gini_result(service, population) # EquityResult: value, method, n_areas, …
score_res = compute_score(
terms={"coverage": 0.75, "evening": 0.50, "night": None},
weights={"coverage": 0.50, "evening": 0.30, "night": 0.20},
)
print(score_res.score, score_res.dropped, score_res.note)
Documentation
License
Metadata
Release files for moveq-core 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| moveq_core-0.2.0.tar.gz | 23.1 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| moveq_core-0.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 36.2 kB
Release files / moveq_core-0.2.0.tar.gz
| Download URL | moveq_core-0.2.0.tar.gz |
|---|---|
| Size | 23.1 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
d53bafd1dce17e4ee516b0c1cc2e9d10e80683e73d59ed7dfe0f45f4e86823de
|
|
BLAKE2b-256 checksum How to use checksums |
584c8e0b1382557f772ecd47db4e277e22cb1fa3d3556a9b68476f3f77ae6eac
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / moveq_core-0.2.0-py3-none-any.whl
| Download URL | moveq_core-0.2.0-py3-none-any.whl |
|---|---|
| Size | 13.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
ddaa30125331ee9c264c7f256539f84d6c2eba59063bc264d746539a9e78ad29
|
|
BLAKE2b-256 checksum How to use checksums |
95839ea62c88faa8e5f61158a73c27e623f2c3510ce8609d05e308aa5c2580e5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency log