add_secondary_travel_matrix
site.SiteProblem.add_secondary_travel_matrix(
travel_matrix_df,
source_col,
label,
skip_cols=None,
unit=None,
from_unit=None,
to_unit=None,
threshold_for_coverage=None,
allow_missing=False,
treat_as_missing=None,
)Register an additional travel/cost matrix for a different mode or scenario (e.g. public transport alongside a primary car matrix).
Unlike add_travel_matrix(), a secondary matrix is never used as the optimisation cost matrix – the primary matrix set via add_travel_matrix() always drives site selection and search/pruning. Instead, each registered secondary matrix contributes its own set of per-solution metric columns (suffixed __<label>, e.g. min_cost__public_transport, weighted_average__public_transport) into every solution solve() produces, so it can be used directly in plots, Metric(column=...), and post-hoc ranking (sort_by="max__public_transport") – without needing to .copy() the problem and solve it twice.
Post-hoc ranking reorders whatever solve() already returned, so it only reorders candidates that survived the primary matrix’s search and pruning. If brute_force_keep_best_n (or _worst_n) is set, candidates were discarded on primary-matrix performance before secondary metrics were ever considered – so a secondary ranking over what survives is not the true best solution for that mode. To search on a secondary matrix rather than merely re-sort by it, pass the column to solve(rank_on=...), which makes the pruning itself use that metric. Alternatively, retain every combination (no brute_force_keep_best_n/_worst_n) and use compute_pareto_front() to explore the trade-off across both matrices at once.
You may call this method multiple times with different labels to register as many secondary matrices as needed. Each one adds approximately the per-candidate cost of evaluating the primary matrix, so solve() time scales close to linearly with the number of secondary matrices registered. Under n_jobs > 1, every registered matrix’s aligned frame is pickled to each worker process alongside the problem, so peak memory also scales with matrix count.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
| travel_matrix_df | pandas.DataFrame or geopandas.GeoDataFrame or str | The dataset containing travel costs for this matrix, or a local or web path to its location. | required |
| source_col | str | The column name in travel_matrix_df that identifies the origin points (should correspond to IDs in the demand data). |
required |
| label | str | A unique label identifying this matrix, used to suffix every metric column it produces (e.g. label="public_transport" -> min_cost__public_transport). Must be non-empty, must not contain "__", and must not already be registered. |
required |
| skip_cols | list of str | A list of column names to ignore during data loading. | None |
| unit | str | A label for the units used in the matrix. Used if no conversion is performed. | None |
| from_unit | (seconds, minutes, hours) | The current time unit of the numeric values in the dataframe. | "seconds" |
| to_unit | (seconds, minutes, hours) | The target time unit for the numeric values in the dataframe. | "seconds" |
| threshold_for_coverage | float | The coverage threshold to apply to this matrix specifically (e.g. 60 minutes for public transport vs 20 for car). If not provided, falls back to the threshold_for_coverage passed to solve() / evaluate_single_solution_single_objective(). |
None |
| allow_missing | bool | Whether to allow missing (NaN) travel costs – a demand location with no feasible journey to a given site. By default a missing value raises KeyError once solve() builds this matrix’s aligned frame (see Notes below); allow_missing=True treats it as genuinely unreachable instead, the same as add_travel_matrix(allow_missing=True). |
False |
| treat_as_missing | scalar or callable | An existing missing-data sentinel already baked into travel_matrix_df (e.g. 9999), converted to a proper missing value before anything else runs – see add_travel_matrix(treat_as_missing=...). |
None |
Returns
| Name | Type | Description |
|---|---|---|
| None |
Raises
| Name | Type | Description |
|---|---|---|
| ValueError | If label is empty, contains "__", or has already been registered. |
|
| KeyError | If source_col is missing from the provided dataframe. |
Notes
Secondary matrices must be complete: every demand location and every candidate site must have a row/column, and (unless allow_missing= True) a non-missing value. This is validated when solve() builds the aligned per-solution frames (not here, since demand/sites may not yet be registered) – see solve() for the specific errors raised for missing rows or missing columns.