networks

Compute network centralities. Two centrality methods are available, using shortest-path (metric) or simplest-path (angular) heuristics:

Metrics are specified as {name: expression} dicts using variables c (cost) and p (normalised progress). For shortest paths, c is metric distance and p = c / threshold. For simplest paths, c is angular cost and p is normalised time progress.

Four categories of metrics are supported:

  • closeness: per-reached-node accumulation (e.g. {"harmonic": "1/c", "density": "1"})
  • betweenness: target seed weight in Brandes backpropagation (e.g. {"betweenness": "1"})
  • cycles: circuit rank (boolean flag)
  • postprocess: derived from computed columns in Python (e.g. {"hillier": "density**2 / farness"})

Pass None for defaults or {} to skip a category.

Per-node weight values (default 1.0, set on the nodes GeoDataFrame or read from NetworkX node attributes) apply gravity-style weighting to centrality: closeness weights each reachable node by its destination weight (so density becomes sum_j w_j rather than a plain count), and betweenness weights each origin-destination pair by the product of its endpoint weights. The same weighting is applied identically whether or not sampling is used. Land-use, mixed-use, and statistical aggregations are intentionally not node-weighted.

When segment_weighted=True, node weights are temporarily set to the primal edge (street segment) lengths so that centrality measures reflect total reachable street length rather than node counts (closeness by destination length, betweenness by the product of endpoint lengths). This is a convenience preset over the per-node weight mechanism and requires a dual graph representation.

When sample=True, only a subset of nodes are used as sources for centrality computation, with results corrected to approximate the full computation.

The reasons for picking one approach over another are varied:

  • Columns prefixed cc_ are managed by cityseer: recomputing a metric for the same distance overwrites the matching cc_ columns in place (intended for re-runs). Don’t store your own data under this prefix.
  • Centralities can be distorted by messy graph topologies such as unnecessary intermediate points along streets (used to describe road curvature) or overly complex representations of street intersections. Clean the network first using the graph module (see the automatic graph cleaning for examples).
  • harmonic centrality can produce inflated values when nodes are very close together, because the inverse-distance calculation amplifies small distances. This is more likely with simplest-path measures or short distance thresholds.
  • Simplest (angular) measures require a dual graph representation. Convert primal graphs with graphs.nx_to_dual before ingesting them.
  • Metrics should only be compared across networks that use the same graph representation (both primal or both dual), because the differing number of nodes and edges between representations affects the metric values. For example, a four-way intersection consisting of one node with four edges on a primal graph translates to four nodes and six edges on the dual. This effect is amplified for denser regions of the network.
  • Standard closeness and normalised closeness do not work well with distance-bounded analysis. Use harmonic closeness or Hillier normalisation instead.

centrality_shortest

centrality_shortest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
closeness : dict[str, str] | None = None
betweenness : dict[str, str] | None = None
cycles : bool = True
postprocess : dict[str, str] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
segment_weighted : bool = False
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute centrality using shortest paths with a single Dijkstra per source. Metrics are specified as {name: expression} dicts. Expressions use two variables:

  • c: the raw cost (metric distance in metres for shortest-path analysis)
  • p: normalised progress from 0 at the source to 1 at the distance threshold (p = c / threshold)

Pass None for defaults or {} to skip a category.

Tip: compute only what you need — a smaller closeness / betweenness dict, {} to skip a whole category, or cycles=False — evaluates fewer expressions and emits fewer columns.

Parameters

network_structure
None

nodes_gdf
None

A GeoDataFrame representing nodes. The outputs of calculations will be written to this GeoDataFrame.

distances
list[int]

Distance thresholds in metres at which to compute centrality measures.

minutes
list[float]

Walking times in minutes; converted to distance thresholds using speed_m_s.

closeness
dict[str, str]

Closeness metric expressions. Each entry is {name: expr(c, p)}, accumulated per reached node. None uses defaults: density, farness, harmonic, decay.

betweenness
dict[str, str]

Betweenness metric expressions. Each entry is {name: expr(c, p)}, used as the weight assigned to each destination when accumulating betweenness contributions along shortest paths. None uses defaults: betweenness, betweenness_decay.

cycles
bool

If True, compute circuit rank (cycle count) for each node. Default True.

postprocess
dict[str, str]

Derived metrics computed in Python from the closeness/betweenness results. None uses default: {"hillier": "density**2 / farness"}.

speed_m_s
float

Speed in metres per second for converting minutes to distance thresholds.

tolerance
float

Relative tolerance for betweenness path equality, as a percentage (e.g. 1.0 = 1%).

segment_weighted
bool

If True, weight by primal edge (street segment) lengths. Requires a dual graph.

random_seed
int

Optional seed for reproducible sampling.

sample
bool

If True, enables adaptive sampling at longer distance thresholds.

epsilon
float

Error tolerance for sampling. Defaults to sampling.HOEFFDING_EPSILON (0.05).

Returns

nodes_gdf
GeoDataFrame

The input nodes_gdf parameter is returned with additional centrality columns.

Notes

from cityseer.tools import mock, graphs, io
from cityseer.metrics import networks

G = mock.mock_graph()
G = graphs.nx_simple_geoms(G)
nodes_gdf, edges_gdf, network_structure = io.network_structure_from_nx(G)
nodes_gdf = networks.centrality_shortest(
    network_structure,
    nodes_gdf,
    distances=[400, 800],
)
print(nodes_gdf[["cc_harmonic_400", "cc_betweenness_800"]])

build_od_matrix

build_od_matrix
(
od_df : pandas.DataFrame
zones_gdf : geopandas.geodataframe.GeoDataFrame
network_structure : NetworkStructure
origin_col : str
destination_col : str
weight_col : str
zone_id_col : str | None = None
max_snap_dist : float = 500.0
)->[ OdMatrix ]

Build an OdMatrix from OD flow data and zone boundaries. Computes zone centroids, snaps them to the nearest network nodes, and constructs a sparse OD weight matrix for use with betweenness_od.

Parameters

od_df
pd.DataFrame

Origin-destination flow data with columns for origin zone, destination zone, and weight.

zones_gdf
gpd.GeoDataFrame

Zone boundaries (polygons) or centroids (points). Must be in a projected CRS matching the network, or in EPSG

(will be auto-reprojected).

network_structure
rustalgos.graph.NetworkStructure

The network to snap zone centroids to.

origin_col
str

Column in od_df containing origin zone identifiers.

destination_col
str

Column in od_df containing destination zone identifiers.

weight_col
str

Column in od_df containing trip weights (e.g., number of bicycle commuters).

zone_id_col
str | None

Column in zones_gdf containing zone identifiers matching origin_col/destination_col. If None, uses the GeoDataFrame index.

max_snap_dist
float

Maximum distance (in CRS units, typically metres) for snapping a centroid to a network node. Centroids beyond this distance are excluded with a warning.

Returns

rustalgos.centrality.OdMatrix

Sparse OD matrix ready for use with betweenness_od.

betweenness_od

betweenness_od
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
od_matrix : OdMatrix
distances : list[int] | None = None
minutes : list[float] | None = None
betweenness : dict[str, str] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
)->[ GeoDataFrame ]

Compute OD-weighted betweenness centrality using the shortest path heuristic.

Parameters

network_structure
None

nodes_gdf
None

A GeoDataFrame representing nodes. The outputs of calculations will be written to this GeoDataFrame.

od_matrix
None

An OdMatrix mapping (origin, destination) node pairs to trip weights. Build with build_od_matrix.

distances
list[int]

Distance thresholds in metres at which to compute betweenness.

minutes
list[float]

Walking times in minutes; converted to distance thresholds using speed_m_s.

betweenness
dict[str, str]

Betweenness metric expressions. None uses defaults: betweenness, betweenness_decay.

speed_m_s
float

Speed in metres per second for converting minutes to distance thresholds.

tolerance
float

Relative tolerance for path equality, as a percentage.

Returns

nodes_gdf
GeoDataFrame

The input nodes_gdf parameter is returned with additional betweenness columns.

betweenness_demand

betweenness_demand
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
origins_gdf : geopandas.geodataframe.GeoDataFrame
destinations_gdf : geopandas.geodataframe.GeoDataFrame
origin_weight_col : str
destination_weight_col : str
distances : list[int] | None = None
minutes : list[float] | None = None
decay_fn : str = 'exp(-4 * p)'
closest_destination : bool = False
metric_name : str = 'demand'
max_snap_dist : float = 100.0
speed_m_s : float = 1.33333
tolerance : float | None = None
)->[ GeoDataFrame ]

Compute demand-weighted (flow) betweenness from a spatial interaction model. Trips are allocated between weighted origins (e.g. population) and weighted destinations (e.g. attractors) using a singly (origin-)constrained spatial interaction model, then routed along shortest network paths so that intermediate nodes accumulate the flow that passes through them. For each origin :math:o and reachable destination :math:d the allocated flow is

.. math:: W_{od} = W_o \cdot \frac{W_d \cdot f(c_{od})}{\sum_{d’} W_{d’} \cdot f(c_{od’})}

where :math:f is decay_fn and :math:c_{od} is the network distance. Each origin’s full weight is conserved and distributed across reachable destinations (destination totals are not constrained — that would require a doubly-constrained / Furness model). The gravity model is the classic instance of this form, recovered with an exponential decay_fn.

This is the modelled-matrix counterpart to betweenness_od: rather than supplying an explicit OD matrix, the per-pair weights are derived from the network distances revealed during routing, computed in a single traversal per origin.

Parameters

network_structure
None

nodes_gdf
None

A nodes GeoDataFrame; flow betweenness columns are written to it and it is returned.

origins_gdf
None

A GeoDataFrame of demand origins (points or centroids).

destinations_gdf
None

A GeoDataFrame of demand destinations / attractors (points or centroids).

origin_weight_col
None

Column in origins_gdf giving each origin’s weight (e.g. population).

destination_weight_col
None

Column in destinations_gdf giving each destination’s attractiveness weight.

distances
list[int]

Distance thresholds in metres at which to compute flow betweenness.

minutes
list[float]

Walking times in minutes; converted to distance thresholds using speed_m_s.

decay_fn
str

Distance-decay expression for the allocation, using c (metric cost) and p (normalised progress = c / threshold). Defaults to "exp(-4 * p)" (scale-free, re-normalised per threshold). For a classic gravity model on absolute distance use e.g. "exp(-0.002 * c)".

closest_destination
bool

If True, each origin routes its full weight to its single nearest reachable destination instead of allocating across all of them.

metric_name
str

Name used for the output column (cc_{metric_name}_{distance}). Defaults to "demand".

max_snap_dist
float

Maximum distance for snapping origin/destination points to network nodes. Points farther than this are dropped (with a logged count).

speed_m_s
float

Speed in metres per second for converting minutes to distance thresholds.

tolerance
float

Relative tolerance for shortest-path equality, as a percentage.

Returns

nodes_gdf
GeoDataFrame

The input nodes_gdf with a flow-betweenness column added per distance threshold.

centrality_simplest

centrality_simplest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
closeness : dict[str, str] | None = None
betweenness : dict[str, str] | None = None
postprocess : dict[str, str] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
segment_weighted : bool = False
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute centrality using simplest (angular) paths with a single Dijkstra per source. Expressions use c (angular cost) and p (normalised time progress).

Tip: compute only what you need — pass a smaller closeness / betweenness dict, or {} to skip a whole category — to evaluate fewer expressions and emit fewer columns.

Parameters

network_structure
None

nodes_gdf
None

A GeoDataFrame representing nodes. The outputs of calculations will be written to this GeoDataFrame.

distances
list[int]

Distance thresholds in metres at which to compute centrality measures.

minutes
list[float]

Walking times in minutes; converted to distance thresholds using speed_m_s.

closeness
dict[str, str]

Closeness metric expressions. None uses defaults: density, farness, harmonic.

betweenness
dict[str, str]

Betweenness metric expressions. None uses defaults: betweenness.

postprocess
dict[str, str]

Derived metrics. None uses default: {"hillier": "density**2 / farness"}.

speed_m_s
float

Speed in metres per second for converting minutes to distance thresholds.

tolerance
float

Relative tolerance for angular betweenness path equality, as a percentage.

segment_weighted
bool

If True, weight by primal edge (street segment) lengths. Requires a dual graph.

random_seed
int

Optional seed for reproducible sampling.

sample
bool

If True, enables adaptive sampling at longer distance thresholds.

epsilon
float

Error tolerance for sampling. Defaults to sampling.HOEFFDING_EPSILON (0.05).

Returns

nodes_gdf
GeoDataFrame

The input nodes_gdf parameter is returned with additional centrality columns.

Notes

from cityseer.tools import mock, graphs, io
from cityseer.metrics import networks

G = mock.mock_graph()
G = graphs.nx_simple_geoms(G)
G_dual = graphs.nx_to_dual(G)
nodes_gdf, edges_gdf, network_structure = io.network_structure_from_nx(G_dual)
nodes_gdf = networks.centrality_simplest(
    network_structure,
    nodes_gdf,
    distances=[400, 800],
)
print(nodes_gdf[["cc_harmonic_400_ang", "cc_betweenness_800_ang"]])

closeness_shortest

closeness_shortest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute closeness centrality using shortest paths. Wraps centrality_shortest with betweenness disabled.

closeness_simplest

closeness_simplest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute closeness centrality using simplest (angular) paths. Wraps centrality_simplest with betweenness disabled.

betweenness_shortest

betweenness_shortest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute betweenness centrality using shortest paths. Wraps centrality_shortest with closeness disabled.

betweenness_simplest

betweenness_simplest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
minutes : list[float] | None = None
speed_m_s : float = 1.33333
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Compute betweenness centrality using simplest (angular) paths. Wraps centrality_simplest with closeness disabled.

node_centrality_shortest

node_centrality_shortest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
betas : list[float] | None = None
minutes : list[float] | None = None
compute_closeness : bool = True
compute_betweenness : bool = True
min_threshold_wt : float = 0.01831563888873418
speed_m_s : float = 1.33333
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Deprecated 4.24 alias for centrality_shortest.

Deprecated

Since version 5.0. Use centrality_shortest with closeness / betweenness expression dicts. This shim preserves the 4.24 output (columns cc_density, cc_farness, cc_harmonic, cc_beta, cc_cycles, cc_hillier, cc_betweenness, cc_betweenness_beta) and will be removed in a future major release. See COMPATIBILITY.md.

node_centrality_simplest

node_centrality_simplest
(
network_structure : NetworkStructure
nodes_gdf : geopandas.geodataframe.GeoDataFrame
distances : list[int] | None = None
betas : list[float] | None = None
minutes : list[float] | None = None
compute_closeness : bool = True
compute_betweenness : bool = True
min_threshold_wt : float = 0.01831563888873418
speed_m_s : float = 1.33333
angular_scaling_unit : float = 90
farness_scaling_offset : float = 1
tolerance : float | None = None
random_seed : int | None = None
sample : bool = False
epsilon : float | None = None
)->[ GeoDataFrame ]

Deprecated 4.24 alias for centrality_simplest.

Deprecated

Since version 5.0. Use centrality_simplest with closeness / betweenness expression dicts. This shim preserves the 4.24 output (angular columns cc_density_ang, cc_farness_ang, cc_harmonic_ang, cc_hillier_ang, cc_betweenness_ang) and will be removed in a future major release. See COMPATIBILITY.md.

segment_centrality

segment_centrality
(
*_args
**_kwargs
)->[ GeoDataFrame ]

Removed in 5.0; raises with guidance.

Deprecated

Since version 5.0. The continuous-segment engine (segment_density / harmonic / beta / betweenness) was removed at the low level, so the old numbers cannot be reproduced. The nearest equivalent is centrality_shortest(..., segment_weighted=True) — a different calculation. See COMPATIBILITY.md.