A Python package for handling and analyzing geographical flow data, extending pandas and geopandas with flow-specific operations.
GeoFlowKit provides FlowSeries and FlowDataFrame types, which are subclasses of pandas.Series and pandas.DataFrame respectively. They are designed to work with flow data consisting of origin-destination (OD) pairs, similar to how geopandas.GeoSeries and geopandas.GeoDataFrame work with geometries.
pip install geoflowkitOr install from source:
pip install .- shapely
- numpy
- pandas
- geopandas >= 1.0.1
- matplotlib
- scikit-learn
- tqdm
- numba
- networkx >= 2.6
import numpy as np
from geoflowkit import Flow, FlowSeries, FlowDataFrame
# Create a single Flow (origin-destination pair)
flow = Flow([[0, 0], [1, 1]])
# Access origin and destination points
print(flow.o) # POINT (0 0)
print(flow.d) # POINT (1 1)# From a list of Flow objects
fs = FlowSeries([
Flow([[0, 0], [1, 1]]),
Flow([[1, 1], [2, 2]]),
Flow([[2, 2], [3, 3]])
], crs="EPSG:4326")
# From coordinate arrays using flows_from_od
from geoflowkit import flows_from_od
o_points = np.array([[0, 0], [1, 1], [2, 2]])
d_points = np.array([[1, 1], [2, 2], [3, 3]])
fs = flows_from_od(o_points, d_points, crs="EPSG:4326")# Create a FlowDataFrame with attributes
data = {
'id': [1, 2, 3],
'value': [10, 20, 30],
'geometry': fs
}
fdf = FlowDataFrame(data, crs="EPSG:4326")
print(fdf)# Read from CSV (specify origin/destination columns)
fdf = read_csv(
'flow_data.csv',
use_cols=['ox', 'oy', 'dx', 'dy'],
crs='EPSG:4326'
)
# Read from GeoPackage
fdf = read_file('flow_data.gpkg', layer='flows')# Access origin and destination points
origins = fdf.o # GeoSeries of origin points
destinations = fdf.d # GeoSeries of destination points
# Flow length and angle
lengths = fdf.length # Distance from origin to destination
angles = fdf.angle # Direction of flow (radians)
# Flow density and volume
density = fdf.density # Flows per unit area
volume = fdf.volume # Total bounding area# Calculate pairwise distances between flows
from geoflowkit import pairwise_distances
dist_matrix = pairwise_distances(fdf, distance='max')
# Calculate local density of flow
from geoflowkit import k_neighbor_distances, snn_distance
k_dists = k_neighbor_distances(fdf, k=2)
snn_dist = snn_distance(fdf, k=8)
# Calculate disorder of flows
from geoflowkit import flow_entropy, flow_divergence
entropy = flow_entropy(fdf)
div = flow_divergence(fdf, n_directions=6)# Clip flows within a polygon
clipped = fdf.clip(polygon_mask)
# Select flows within bounds
within_bounds = fdf.within(bounds_box)
# Calculate the distance with others
dist_series = fdf.distance(flow)
dist_series = fdf.distance(other_fdf)# Calculate K function for spatial clustering
from geoflowkit import k_func, l_func
r_list, kr_list = k_func(fdf, dr=0.1, k=1)
r_list, lr_list = l_func(fdf, dr=0.1, k=1)
# Local L function for individual flows
from geoflowkit import local_l_func
llrs = local_l_func(fdf, r=0.5)# Divide study area into grid and aggregate flows
gridded = fdf.to_grid(delta_x=0.1, delta_y=0.1)# Plot flows as arrows
ax = fdf.plot(kind='arrow', column='value')
# Plot FlowSeries
ax = fs.plot()from geoflowkit.visualization import ODMatrixVisualizer, MapTrixVisualizer
# OD Matrix heatmap (colour = flow count)
ODMatrixVisualizer(
origin_zones=border, zone_id_col='Name',
weight='count', cmap='OrRd',
).fit_plot(fdf, ax=ax)
# OD Matrix with proportional circles (size = flow length)
ODMatrixVisualizer(
origin_zones=border, zone_id_col='Name',
weight='count', size_weight='length',
).fit_plot(fdf, ax=ax)
# MapTrix — rotated matrix + origin/destination maps
MapTrixVisualizer(
origin_zones=border, zone_id_col='Name',
weight='count', size_weight='length',
out_title='Outflow', in_title='Inflow',
).fit_plot(fdf, figsize=(16, 9))# K-medoid clustering
from geoflowkit import kmedoid
labels = kmedoid(fdf, n_clusters=5)
# DBSCAN clustering
from geoflowkit import dbscan
labels = dbscan(fdf, eps=0.5, min_samples=5)
# K-Means clustering (virtual flow centers)
from geoflowkit import kmeans
labels = kmeans(fdf, n_clusters=5, distance='max', random_state=42)
# Or use the class API to inspect cluster centers
from geoflowkit import KMeansFlow
km = KMeansFlow(n_clusters=5, distance='max', random_state=42).fit(fdf)
print(km.cluster_centers_) # FlowSeries of virtual flow centersCommunity detection algorithms identify groups of closely connected zones in flow networks. These algorithms first build a flow network graph from the FlowDataFrame, then detect communities using modularity optimization.
# CNM (Clauset-Newman-Moore) algorithm
from geoflowkit import cnm
labels = cnm(fdf, zone_method='grid', cell_size=1000)
# Louvain algorithm
from geoflowkit import louvain
labels = louvain(fdf, zone_method='grid', cell_size=1000, seed=42)
# STOCS (Spatial Tabu Optimization for Community Structure)
from geoflowkit import stocs
labels = stocs(
fdf, zone_method='grid', cell_size=1000,
spatial_weight=0.5, tabu_tenure=15
)Zone methods: 'grid' (regular grid), 'aggregate' (unique OD pairs), 'gdf' (external GeoDataFrame), 'custom' (user function).
# Custom zone function example
import numpy as np
def my_zones(fdf):
origins = np.array([[p.x, p.y] for p in fdf.o])
destinations = np.array([[p.x, p.y] for p in fdf.d])
o_zones = (origins[:, 0] > 0).astype(int) * 2 + (origins[:, 1] > 0).astype(int)
d_zones = (destinations[:, 0] > 0).astype(int) * 2 + (destinations[:, 1] > 0).astype(int)
zone_centroids = {i: (origins[o_zones == i].mean(axis=0)) for i in range(4) if (o_zones == i).any()}
return o_zones.astype(int), d_zones.astype(int), zone_centroids
labels = louvain(fdf, zone_method='custom', zone_func=my_zones)Output: Flow-level labels where -1 indicates cross-community flows (origin and destination belong to different communities).
The I-index quantifies the irreplaceability of a location based on flows, combining flow volume and flow length into a single metric following the H-index principle.
from geoflowkit.spatial import i_index
# Calculate I-index for each zone
result = i_index(fdf, zones)
# Using origin points instead of destination
result = i_index(fdf, zones, od_type='o')
# With custom alpha parameter
result = i_index(fdf, zones, alpha=1000.0)I-index definition: The I-index of a location is the maximum value of i such that at least i flows with a length of at least α × i meters have reached this location. Higher values indicate more irreplaceable locations that attract many long-distance flows.
Jupyter notebook examples are available in the examples/ folder:
- basic_usage.ipynb - Basic usage of Flow, FlowSeries, and FlowDataFrame
- clustering.ipynb - Flow clustering and community detection
- kl_function.ipynb - K/L functions for spatial clustering detection
- maptrix_demo.py - OD Matrix and MapTrix visualization demo
- centrality.ipynb - I-index for location irreplaceability
Flow: Geometry object representing an origin-destination pairFlowSeries: pandas Series subclass for storing Flow objectsFlowDataFrame: pandas DataFrame subclass with Flow geometry columnKMedoidFlow: K-medoid clustering for flow dataDBSCANFlow: DBSCAN clustering for flow dataKMeansFlow: K-Means clustering for flow data (virtual flow centers)CNMFlow: Clauset-Newman-Moore community detectionLouvainFlow: Louvain community detectionSTOCSFlow: Spatial Tabu Optimization for Community StructureODMatrixVisualizer: OD matrix heatmap visualizationMapTrixVisualizer: MapTrix layout (matrix + maps + guide lines)
flows_from_od(o, d, crs=None): Create FlowSeries from coordinate arraysflows_from_geometry(geometry, crs=None): Create FlowSeries from geometry objectsread_csv(file_path, use_cols, crs=None, **kwargs): Read flow data from CSVread_file(file_path, **kwargs): Read flow data from vector filepairwise_distances(fdf, distance='max', ...): Calculate flow distance matrixk_neighbor_distances(fdf, k, distance='max', ...): K-order nearest neighbor distancessnn_distance(fdf, k, ...): Shared nearest neighbor distanceflow_entropy(fdf, cell_area=None, ...): Flow space entropyflow_divergence(fdf, n_directions=6, ...): Flow directional entropyk_func(fdf, dr, k=1, distance='max', ...): K function for spatial clustering detectionl_func(fdf, dr, k=1, distance='max', ...): L function for spatial clustering detectionlocal_l_func(fdf, r, distance='max', ...): Local L function for individual flowskmedoid(fdf, n_clusters=5, ...): K-medoid clustering for flowsdbscan(fdf, eps=0.5, min_samples=5, ...): DBSCAN clustering for flowskmeans(fdf, n_clusters=8, distance='max', ...): K-Means clustering for flows (virtual flow centers)cnm(fdf, zone_method='grid', cell_size=..., ...): CNM community detectionlouvain(fdf, zone_method='grid', cell_size=..., seed=..., ...): Louvain community detectionstocs(fdf, zone_method='grid', cell_size=..., spatial_weight=0.5, ...): STOCS community detectioni_index(fdf, zones, alpha=None, od_type='d', ...): I-index for location irreplaceabilitysecond_order_density(fdf, ...): Second-order density of flowsFlowSeries / FlowDataFrame methods:plot(): Render flows as arrows using matplotlib quiverwithin(mask): Select flows whose origin and destination are both inside maskclip(mask): Clip flows to a mask polygondistance(other, distance='max', ...): Calculate distance to another flow or FlowSeriesto_grid(delta_x=None, delta_y=None, ...): Divide study area into grid and aggregate flows (FlowDataFrame only)to_crs(crs): Transform CRS of flowsset_crs(crs, allow_override=False): Set CRS without transforming geometries
GeoFlowKit is licensed under the MIT License.
For questions or feedback: djw@lreis.ac.cn