Quickstart¶
Describing a decision¶
A decision is a matrix of options (rows) scored on criteria (columns), plus a
Criterion per column saying what that column means.
from mcdakit import Criterion
criteria = [
Criterion("Price", weight=0.40, direction="cost", bounds=(2.00, 4.00)),
Criterion("Quality", weight=0.25, direction="benefit", bounds=(0, 10)),
Criterion("Lead time", weight=0.20, direction="cost", bounds=(5, 35)),
Criterion("Support", weight=0.15, direction="benefit", bounds=(0, 10)),
]
direction="cost" says smaller is better. Enter figures as measured —
real euros, real days — and let the library handle direction. Pre-inverting
them yourself will double-invert and silently produce the wrong answer.
bounds is the range the criterion could take, independent of the options
on the table: a budget ceiling, a rating scale, an acceptable delivery window.
Optional everywhere except SPOTIS, whose guarantee depends on it.
weight is relative. 40/30/20/10 and 0.4/0.3/0.2/0.1 are the same input.
Ranking¶
from mcdakit import rank
matrix = [
[2.75, 7.0, 14, 8.0],
[2.90, 8.5, 16, 8.0],
[3.40, 9.0, 11, 7.0],
[2.20, 3.0, 32, 2.0],
]
labels = ["Option 1", "Option 2", "Option 3", "Option 4"]
result = rank(matrix, criteria, method="spotis", labels=labels)
result.winner # 'Option 1'
result.order # best first
result.ranking # [(label, score), ...]
result.scores # in the original row order
result.rank_of("Option 2")
print(result) # a readable table
Scores always read higher is better, including for VIKOR and SPOTIS whose native measures are smallest-is-best.
Asking how stable it is¶
from mcdakit import sensitivity
report = sensitivity(result)
report["level"] # 'fragile' | 'moderate' | 'robust' | 'immovable'
report["weakest"] # the criterion with least room to move
report["overall"] # smallest tolerance across criteria
for row in report["rows"]:
print(row["name"], row["tolerance"], row["direction"], row["flips_to"])
Each row says how far that weight must move — and which way — before a different option wins. “Quality can move by 7.5%” is unusable without knowing that means down.
Only one weight moves at a time, so this is an upper bound on stability: a simultaneous shift could unseat the winner sooner.
Comparing methods¶
from mcdakit import compare_methods, agreement
results = compare_methods(matrix, criteria, labels=labels)
agreement(results)
# {'winners': {...}, 'consensus': ..., 'tied': (...), 'votes': 7, 'of': 17,
# 'unanimous': False}
When methods disagree, that disagreement is the finding: the options are close enough that the modelling choice decides the outcome.
Weights from pairwise comparison¶
from mcdakit import ahp_weights
out = ahp_weights(
{(0, 1): 3, (0, 2): 9, (1, 2): 3},
names=["Price", "Quality", "Delivery"],
)
out["weights"] # [0.6923, 0.2308, 0.0769]
out["consistency_ratio"] # 0.0
out["consistent"] # True
The consistency ratio is reported, never enforced.