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:

  1. Normalize each column of scores to [0, 1] using the per-metric strategy in normalization (default min_max on every column), respecting per-metric polarity and, when provided, declared bounds. After this step every column is oriented so higher is better.
  2. Build a weight vector. Pass "equal", "entropy", "std", "critic" or "merec" for the built-in objective schemes, or pass an explicit array of length n_metrics. For a subjective scheme, call beam.mcda.ahp_weights on 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).
  3. 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]