return_solution_metrics
site.EvaluatedCombination.return_solution_metrics(full_secondary_metrics=False)Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| full_secondary_metrics | bool | If False (the default), each registered secondary travel matrix contributes only its core five metrics plus the float-valued equity aggregations (see point 6 below) – matching the output shape prior to this parameter’s introduction. If True, every registered secondary matrix also contributes its dict-valued equity breakdowns (weighted_by_equity_group__<label>, etc.) and description strings, exactly mirroring what the primary matrix already always returns unsuffixed. This costs nothing extra to compute – _compute_travel_metrics already produces these values for every matrix regardless – it only changes which of the already-computed keys get included here. |
False |
Notes
INTERPRETATION GUIDE FOR SUMMARY TABLES & SORTING:
1a. ‘weighted_average’ LOWER is better. Represents travel time adjusted for specified weighting factors.
1b. Travel Costs (‘unweighted_average’, ‘90th_percentile’, ‘max’): LOWER is better. Represents travel time or distance.
1c. ‘total_cost’ LOWER is better. Total fixed cost of the selected sites (sum of the cost_col configured via add_sites()). NaN if no cost_col was configured. Only influences which solution is selected if explicitly passed as a weight (weights={“cost”: …}).
Absolute Equity Gap (‘gap_absolute_weighted’): CLOSER TO 0 is better. Measures the flat minute/distance difference between the best-served and worst-served equity bands. High numbers mean severe geographical disparity.
Relative Equity Gap (‘gap_relative_weighted’): CLOSER TO 1.0 is better. If it’s 1.5, the worst-served group travels 1.5x longer than the best-served group.
Inter-Tertile Ratio (‘inter_tertile_ratio’): Measures macro-equity between the most- and least-disadvantaged equity-band tertiles, per
add_equity_data(disadvantaged_end=...)– lower bins = more disadvantaged (e.g. IMD 1-3) whendisadvantaged_end="low"or unset; the reverse when"high". SORTING CRITERIA:- ITR > 1.0: Inequity. The most deprived third faces longer travel times than the least deprived third (e.g., 1.25 = 25% longer travel).
- ITR = 1.0: Perfect equality in macro travel times.
- ITR < 1.0: Progressive equity. Travel times are shorter for the most deprived communities.
Coverage Metrics (‘proportion_within_coverage_threshold’, ‘coverage_by_equity_group’): HIGHER is better (Scale: 0.0 to 1.0). Represents accessibility. Look for solutions where coverage is both globally high and uniformly distributed across groups. Both are
NaNif nothreshold_for_coveragewas given.NAMING RULE: an unqualified coverage metric is weighted by the demand registered via
add_demand(), so it answers “what share of people are covered”. Theregionsvariants (‘proportion_regions_within_coverage_threshold’, ‘coverage_regions_by_equity_group’) count every region equally and answer “what share of places are covered”. The two coincide when demand is uniform, including whenadd_demand()was never called.The demand-weighted figure is the one ‘mclp’ optimises, matching the textbook Maximal Covering Location Problem.
5b. Absolute coverage headcounts (‘demand_within_coverage_threshold’, ‘regions_within_coverage_threshold’): HIGHER is better. The literal headcount/region-count behind the two coverage proportions above – e.g. “391,823 people” rather than “75.4%” – for reporting to audiences who find an absolute number more legible than a percentage. NaN/0 under the same conditions as the proportions.
5c. Unreachable headcounts (‘regions_unreachable’, ‘demand_unreachable’, ‘proportion_demand_unreachable’): LOWER is better. How many regions / how much demand had no feasible journey to any selected site at all – a missing (NaN) travel cost, only possible once a travel matrix was registered with add_travel_matrix(allow_missing=True). 0/0.0 when every region reached at least one selected site (including every problem that never opted into allow_missing). Distinct from – and independent of – threshold_for_coverage: a region can be “beyond threshold” while still reachable, or unreachable with no threshold ever configured. weighted_average/unweighted_average/90th_percentile/max above are computed over reachable regions only, rather than this headcount silently dragging them toward whatever value an unreachable pair happened to hold.
5d. Ranking-only travel cost (‘weighted_average_for_ranking’, ‘unweighted_average_for_ranking’, ‘max_for_ranking’): LOWER is better – but NOT a reporting metric. Identical to ‘weighted_average’/‘unweighted_average’/‘max’ unless solve(unreachable_cost=...) was used, in which case every unreachable row is substituted with unreachable_cost instead of excluded, matching what solve() actually ranked/pruned combinations on. Kept alongside the honest figures rather than replacing them so the two are always visibly distinguishable: the reachable-only numbers describe what really happened, these describe what the search optimised. Present regardless of whether unreachable_cost was used – identical to their honest counterparts when it wasn’t, so this column always exists and needs no conditional handling.
Secondary travel matrices (columns suffixed
__<label>, e.g. ’weighted_average__public_transport’):Registered via
add_secondary_travel_matrix(label=...). Same metrics and sort direction as their unsuffixed counterparts above (1a/1b/5/5c), computed against that matrix’s own travel costs instead of the primary matrix. By default, only the core scalar metrics (both coverage proportions and the unreachable headcounts included) plus the float-valued equity aggregations (gap_absolute_weighted, gap_relative_weighted, avg_*_third_bins, inter_tertile_ratio) are included per matrix, to keep this table from growing unboundedly with each registered matrix – passfull_secondary_metrics=Trueto also include the dict-valued equity breakdowns and description strings, matching what the primary matrix already returns. The underlying per-regionproblem_dfalways carriesmin_cost__<label>/selected_site__<label>/within_threshold__<label>regardless of this setting.Population impact vs baseline (
demand_improved,demand_worsened,demand_unchanged,proportion_demand_ improved,proportion_demand_worsened,regions_improved,regions_worsened,regions_unchanged,mean_reduction_among_improved,mean_increase_among_worsened,max_reduction,max_increase):Only present when a baseline was supplied (
solve(baseline=...), or a baseline was passed toevaluate_single_solution_single_ objective()directly) – absent otherwise, notNaN, sosolution_df’s schema is unchanged for callers that never ask for a baseline.demand_*/mean_*are HIGHER-is-better for the_improved/reductionnames (more people better off, or a bigger improvement) and LOWER-is-better for the_worsened/increasenames;*_unchangedis directionless. SeeSolutionComparator.population_impact_summary()for the equivalent baseline-vs-candidate comparison without solving viasolve(baseline=...). Secondary travel matrices and secondary demand scenarios get only the demand-weighted subset (demand_improved__<label>,demand_worsened__<label>,demand_unchanged__<label>,mean_reduction_among_improved__ <label>,mean_increase_among_worsened__<label>) by default;full_secondary_metrics=Truealso adds the region counts and max change (regions_improved__<label>, etc.) for secondary travel matrices. Demand scenarios never get the region-count/max variants (region membership doesn’t vary with demand), matching point 6’s “only what varies with demand” rule.When equity data is also registered,
demand_improved_by_equity_ group/demand_worsened_by_equity_group(dict per band) are also added – e.g. “are the most deprived areas improving at a higher rate than the least deprived?”. Primary matrix only; not yet broken down for secondary travel matrices/demand scenarios.
7b. Which sites actually changed (sites_closed_vs_baseline, sites_added_vs_baseline):
`sites_closed_vs_baseline` is the baseline's own site names
absent from this solution's `site_names`; `sites_added_vs_
baseline` is `site_names` absent from the baseline's. Answers
"which sites changed?" directly, rather than requiring a caller
to compute the set difference themselves -- useful when several
solutions share nearly all the same sites and only differ in
one or two, which a bare `site_names` column doesn't make
obvious at a glance. Present under the same condition as the
rest of this point (a baseline was supplied); absent, not empty
lists, otherwise.
“Left behind” headcounts (
demand_beyond_threshold_<t>,regions_beyond_threshold_<t>, one pair per thresholdtinbeyond_thresholds, plus a_by_equity_groupdict variant of each when equity data is registered):LOWER is better – the opposite direction from the coverage metrics in point 5, since these count people/regions a threshold crossing leaves outside rather than within. A distinct concept from
threshold_for_coverage/‘coverage metrics’ even though both compare a travel cost against a cutoff: “covered” (good, one threshold) vs “beyond” (bad, any number of thresholds at once). Thresholds are independent, not cumulative –demand_beyond_threshold_45is not a subset ofdemand_beyond_threshold_30’s complement; each is a fresh count against its own cutoff. Only present whenbeyond_thresholdswas supplied – absent, notNaN, otherwise. Secondary travel matrices get the demand-weighted headcount by default (demand_beyond_threshold_<t>__<label>);full_secondary_metrics=Truealso adds region counts and the equity breakdowns, matching point 6/7’s pattern. Secondary demand scenarios get only the demand-weighted headcount, never region counts or equity breakdowns, matching point 6’s “only what varies with demand” rule.Additional sites chosen beyond required (
additional_site_names):site_nameswith the required sites (flagged viaadd_sites(required_sites_col=...)) removed – e.g. for a “we have 4 sites and are opening 1 more” problem, this is just the new site, rather than all 5 names, which otherwise look identical across every solution that all correctly keep the same 4 required sites. Only present when at least one required site is configured; absent, not an empty list, otherwise.Sites not selected (
unselected_site_names):Every registered candidate site absent from
site_names, in canonical site-index order – the complement ofsite_namesamong ALL candidates, not just the required ones point 9 removes. Always present (unlike point 9), since it doesn’t depend on any optional registered data.