Evaluation Measures
| API | Description | Example |
|---|---|---|
ComparisonResult |
Result of compare_to: an overall value and, if grouped, a per-group breakdown. |
|
common_part_of_commuters |
Return the common part of commuters (CPC) between observed and predicted flows. | Common part of commuters and links |
common_part_of_commuters_distance |
Compare the trip-distance distributions of two sets of trips (CPCD). | Common part of commuters and links |
common_part_of_links |
Return the common part of links (CPL) between observed and predicted flows. | Common part of commuters and links |
compare_flow_models |
Score one or several flow models against observed origin-destination flows. | Agreement with the reference |
compare_to |
Compare value_col between two dataframes, optionally grouped by group_col. |
Compare by group |
information_gain |
Return the information gain (KL divergence variant) of true over pred. | Information and distribution metrics |
jensen_shannon_divergence |
Return Jensen-Shannon divergence between two distributions. | Information and distribution metrics |
kullback_leibler_divergence |
Return the Kullback–Leibler divergence (D_\mathrm{KL}(\mathrm{true}\,\ | \,\mathrm{pred})). |
max_error |
Return the maximum signed error max(true_i - pred_i). | Error metrics on matched flows |
mse |
Return the mean squared error between true and predicted values. | Error metrics on matched flows |
nrmse |
Return the normalized root mean squared error (RMSE / sum(true)). | Error metrics on matched flows |
r_squared |
Return the coefficient of determination R². | Error metrics on matched flows |
rmse |
Return the root mean squared error between true and predicted values. | Error metrics on matched flows |
stvd_emd |
Compute the spatio-temporal Wasserstein distance between two distributions. | Compare when things happen |
wasserstein_distance |
Return the 1-D Wasserstein (earth mover's) distance between two empirical samples. | Information and distribution metrics |
evaluation
fastmob.measures.evaluation — generic comparison metrics.
For grouped/dataframe-level comparisons, use BaseDataFrame.compare_to()
(inherited by Staypoints, Trips, Triplegs, Locations,
TrajDataFrame, FlowDataFrame, Tours) instead of hand-coding a
grouping around these primitives.
ComparisonResult
dataclass
Result of compare_to: an overall value and, if grouped, a per-group breakdown.
common_part_of_commuters(observed, predicted)
Return the common part of commuters (CPC) between observed and predicted flows.
CPC is 2 * sum(min(observed, predicted)) / (sum(observed) + sum(predicted)) over origin-destination
pairs: 1 when the two flow tables are identical, 0 when they share no trips. Self-loops and missing
endpoints are ignored.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
observed
|
Trips or FlowDataFrame
|
Two |
required |
predicted
|
Trips or FlowDataFrame
|
Two |
required |
Returns:
| Type | Description |
|---|---|
float
|
CPC between 0 and 1 (higher is more similar). It depends on the zone size: finer zones give lower values. |
common_part_of_commuters_distance(observed, predicted)
Compare the trip-distance distributions of two sets of trips (CPCD).
Distances are grouped in 2 km bins and the result is sum(min(count_observed, count_predicted)) /
number of observed trips: 1 for identical distance distributions, 0 for none in common.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
observed
|
Trips
|
Two |
required |
predicted
|
Trips
|
Two |
required |
Returns:
| Type | Description |
|---|---|
float
|
CPCD between 0 and 1 (higher is more similar when the two sets have similar numbers of trips). |
common_part_of_links(observed, predicted)
Return the common part of links (CPL) between observed and predicted flows.
CPL is 2 * |links in both| / (|links observed| + |links predicted|): the share of origin-destination
links that exist in both tables, whatever their weight. It is 1 when both have exactly the same links.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
observed
|
Trips or FlowDataFrame
|
Two |
required |
predicted
|
Trips or FlowDataFrame
|
Two |
required |
Returns:
| Type | Description |
|---|---|
float
|
CPL between 0 and 1 (higher is more similar). |
compare_flow_models(observed, models, pairs='catalogue')
Score one or several flow models against observed origin-destination flows.
Every model is scored on the same set of pairs, leaving out self-loops, and a pair that is missing from a table
has flow 0 there. By default (pairs="catalogue") the pairs are all the pairs of the locations of
observed, so the score of a model does not depend on which other models are scored with it; report the
n_pairs column with the scores. With pairs="union" they are the pairs that have flow in the observed table
or in any model, which is the only choice when observed has no locations, but then a model's score changes
when another model is added. The Poisson deviance and pseudo R-squared
treat the flows as counts, so compare models whose total flow is on the scale of the observed one (for
example generated with the observed outflows).
Two cautions. The common part of links (CPL) compares which pairs have flow. Expected flows
(out_format="flows") are positive on every pair, so their CPL is fixed by the number of observed pairs and
says nothing about the model; use sampled (integer) flows for CPL. And a model that was fitted to the observed
flows is scored in-sample: compare it with a parameter-free model, or score it on flows that were held out,
before concluding that it is better.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
observed
|
FlowDataFrame
|
Observed flows with |
required |
models
|
FlowDataFrame or mapping of str to FlowDataFrame
|
The model flows to score, by name. A single FlowDataFrame is named |
required |
pairs
|
('catalogue', 'union')
|
The pairs on which the scores are computed, as above. Default |
"catalogue"
|
Returns:
| Type | Description |
|---|---|
Table
|
One row per model with |
Raises:
| Type | Description |
|---|---|
TypeError
|
If |
ValueError
|
If |
See Also
fastmob.models.Gravity.fit : Fit a gravity model and get confidence intervals. common_part_of_commuters : The CPC on its own.
Examples:
>>> import pandas as pd
>>> from fastmob import FlowDataFrame
>>> from fastmob.measures.evaluation import compare_flow_models
>>> table = pd.DataFrame({"origin": ["a", "a", "b"], "destination": ["b", "c", "c"], "flow": [10, 5, 7]})
>>> flows = FlowDataFrame(table, tile_id="tile_id")
>>> compare_flow_models(flows, flows, pairs="union").column("cpc").to_pylist()
[1.0]
compare_to(df1, df2, value_col, *, group_col=None, metric=None)
Compare value_col between two dataframes, optionally grouped by group_col.
metric defaults to wasserstein_distance and accepts any
(array, array) -> float callable, e.g. jensen_shannon_divergence.
When group_col is given, the comparison runs once per label present in
both dataframes (sorted for determinism) and overall is the mean across
groups.
information_gain(true, pred)
Return the information gain (KL divergence variant) of true over pred.
where \(N = \sum_i y_i\).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values (must be positive). |
required | |
pred
|
Estimated target values (must be positive). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Information gain value; 0.0 when the distributions are identical. |
Examples:
jensen_shannon_divergence(distribution1, distribution2)
Return Jensen-Shannon divergence between two distributions.
Inputs are normalised internally (raw counts and probabilities both
work) and must have the same length -- unlike wasserstein_distance,
the two arrays are paired by index (one entry per category/bin), not
independent samples.
kullback_leibler_divergence(true, pred)
Return the Kullback–Leibler divergence \(D_\mathrm{KL}(\mathrm{true}\,\|\,\mathrm{pred})\).
Inputs are normalized to probability distributions before evaluation,
matching the historical scipy.stats.entropy(true, pred) behavior.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Probability distribution P (reference). |
required | |
pred
|
Probability distribution Q (approximation). |
required |
Returns:
| Type | Description |
|---|---|
float
|
KL divergence; 0.0 when the distributions are identical. |
Examples:
max_error(true, pred)
Return the maximum signed error max(true_i - pred_i).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values. |
required | |
pred
|
Estimated target values. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Maximum element-wise difference true - pred. |
Examples:
mse(true, pred)
Return the mean squared error between true and predicted values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values. |
required | |
pred
|
Estimated target values. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Non-negative MSE; 0.0 is the best possible value. |
Examples:
nrmse(true, pred)
Return the normalized root mean squared error (RMSE / sum(true)).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values. |
required | |
pred
|
Estimated target values. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Non-negative NRMSE; 0.0 is the best possible value. |
Examples:
r_squared(true, pred)
Return the coefficient of determination R².
where
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values. |
required | |
pred
|
Estimated target values. |
required |
Returns:
| Type | Description |
|---|---|
float
|
R² score. Best possible value is 1.0; can be negative. |
Examples:
rmse(true, pred)
Return the root mean squared error between true and predicted values.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
true
|
Ground truth target values. |
required | |
pred
|
Estimated target values. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Non-negative RMSE; 0.0 is the best possible value. |
Examples:
stvd_emd(dist_a, dist_b, alpha=10.0, cyclical_period=1440.0, *, time_col=None, weight_col=None, lat_col=None, lng_col=None, reg=0.01, max_iter=200, tol=None)
Compute the spatio-temporal Wasserstein distance between two distributions.
Each input is a DataFrame representing a spatial-temporal distribution with
columns for a time bin (HH:MM string), a weight/volume value, and a WGS84
latitude/longitude centroid — the exact shape produced by
fastmob.measures.collective.build_stvd.
Distances are computed as an entropy-regularised (Sinkhorn) Earth Mover's
Distance over an explicit cost matrix combining the great-circle
(Haversine) distance between centroids with a cyclical-time term: two
points at times t1/t2 contribute 2 * r * sin(d_theta / 2)
metres, where r = alpha * cyclical_period / (2*pi),
theta = 2*pi*(t / cyclical_period), and d_theta is the shortest
angular difference between the two times.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
dist_a
|
Any
|
Any Narwhals-compatible eager DataFrame (pandas, polars, …). |
required |
dist_b
|
Any
|
Any Narwhals-compatible eager DataFrame (pandas, polars, …). |
required |
alpha
|
float
|
Space-time tradeoff: 1 minute equals |
10.0
|
cyclical_period
|
float
|
Wrap-around period in minutes. Default |
1440.0
|
time_col
|
str | None
|
Explicit time column name. Auto-detected when |
None
|
weight_col
|
str | None
|
Explicit weight column name. Auto-detected when |
None
|
lat_col
|
str | None
|
Explicit latitude/longitude column names. Auto-detected when |
None
|
lng_col
|
str | None
|
Explicit latitude/longitude column names. Auto-detected when |
None
|
reg
|
float
|
Sinkhorn entropic regularization strength (same units as the cost
matrix, metres). Smaller values approximate exact optimal transport
more closely but are more prone to numerical underflow. Default
|
0.01
|
max_iter
|
int
|
Maximum number of Sinkhorn dual-update iterations. Default |
200
|
tol
|
float | None
|
Optional marginal-constraint convergence tolerance. When set, the
solver stops early once the dual updates converge to within |
None
|
Returns:
| Type | Description |
|---|---|
float
|
Spatio-temporal Wasserstein distance in metres. |
Examples:
>>> import pandas as pd
>>> from fastmob.measures.evaluation import stvd_emd
>>> dist_a = pd.DataFrame(
... {
... "time_bin": ["08:00", "08:10"],
... "mean_volume": [2.0, 1.0],
... "center_lat": [0.0, 0.0],
... "center_lng": [0.0, 0.001],
... }
... )
>>> dist_b = pd.DataFrame(
... {
... "time_bin": ["08:00", "08:10"],
... "mean_volume": [1.0, 2.0],
... "center_lat": [0.0, 0.0],
... "center_lng": [0.0, 0.002],
... }
... )
>>> print(round(stvd_emd(dist_a, dist_b), 3))
74.156
wasserstein_distance(values1, values2)
Return the 1-D Wasserstein (earth mover's) distance between two empirical samples.
The distance is the average amount by which the values of one sample must be moved to turn it into the
other, in the units of the values. Non-finite values are dropped; the result is nan if a sample is empty.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
values1
|
array - like
|
The two samples (they may have different lengths). |
required |
values2
|
array - like
|
The two samples (they may have different lengths). |
required |
Returns:
| Type | Description |
|---|---|
float
|
Non-negative distance; 0 for identical samples. |
Worked example: Common part of commuters and links · Agreement with the reference · Compare by group · Information and distribution metrics · Error metrics on matched flows · Compare when things happen