run
mcda.run(scores, polarity, weights='equal', method='saw', bounds=None, metric_ids=None, normalization=None, baselines=None, targets=None, missing='error')
Run a full MCDA pipeline from raw scores to per-tool ranks.
Three steps:
- Normalize each column of
scoresto [0, 1] using the per-metric strategy innormalization(defaultmin_maxon every column), respecting per-metric polarity and, when provided, declared bounds. After this step every column is oriented so higher is better. - Build a weight vector. Pass
"equal","entropy","std","critic"or"merec"for the built-in objective schemes, or pass an explicit array of lengthn_metrics. For a subjective scheme, callbeam.mcda.ahp_weightson a pairwise comparison matrix and pass the returned array."merec"takes logarithms and needs a normalization bounded away from zero, so it rejects a column carrying a hard zero (plain min-max maps the worst tool to zero). - Aggregate to one composite score per tool. Pass
"saw"for simple additive weighting (the dot product of normalized scores and weights),"topsis"for distance-to-ideal aggregation,"vikor"for the compromise ranking,"promethee_ii"for the net outranking flow, or"comet"for the characteristic-objects model.
Returns a Result with every intermediate output. Two runs over the same scores can be compared by their .ranks to see how much the choice of weighting or method matters.
Use run_from_registry for the ontology-aware path: it pulls polarity and declared bounds from the metric cards, validates the requested method against the declared scale types, and feeds the result into this function.
Parameters
| Name | Type | Description | Default |
|---|---|---|---|
scores |
Array-like of shape (n_tools, n_metrics). |
required | |
polarity |
Sequence[str] | Length n_metrics sequence of "higher_is_better" or "lower_is_better". Get this from beam.cards.polarities_for(metric_ids). |
required |
weights |
"equal" (default), "entropy", "std", "critic", "merec", or an explicit array of length n_metrics. |
'equal' |
|
method |
str | "saw" (default), "topsis", "vikor", "promethee_ii" or "comet". |
'saw' |
bounds |
Sequence[Bound] | None | Optional list of (lower, upper) per metric. Forwarded to min_max_normalize. Either side can be None to fall back to the empirical extremum. |
None |
metric_ids |
Sequence[str] | None | Optional list of metric ids, carried in the Result for labelling and used to name columns in any normalization warning. | None |
normalization |
None (min_max on every column), a single strategy name applied to all columns, or a per-column sequence. Strategy names are listed in beam.mcda.normalize.STRATEGIES. |
None |
|
baselines |
Sequence[float | None] | None | Optional per-column reference score required by the baseline_relative strategy. Forwarded to normalize. |
None |
targets |
Sequence[float | None] | None | Optional per-column ideal value required by the target_relative strategy. Forwarded to normalize. |
None |
missing |
str | Policy for missing cells (NaN) in the tool by metric matrix, applied after normalization. beam never picks this for you, so the default refuses. One of: - "error" (default): raise IncompleteMatrixError naming the missing cells and the alternatives. - "available": available-case. SAW scores each tool on the metrics it has, with weights renormalized over that tool’s observed support. Only SAW supports this; the distance methods and the objective weightings refuse, since they cannot be defined without completing the matrix. - "worst": treat a non-run as the worst outcome, mapping each missing cell to the worst normalized score (0). The matrix is then complete and every method runs. An explicit failure policy, not imputation of an unknown. - "impute": fill each missing cell with the per-metric mean of the observed normalized scores. Discouraged; provided only because a user may explicitly want it. Every non-error policy records a warning on the Result. |
'error' |
Returns
| Type | Description |
|---|---|
| Result |
Examples
>>> import numpy as np
>>> from beam.mcda import run
>>> scores = np.array([[0.9, 30.0], [0.7, 50.0]])
>>> result = run(scores, ["higher_is_better", "lower_is_better"])
>>> result.ranks.tolist()
[1, 2]