Example 22: Fuzzy DEMATEL¶
Three experts' triangular influence matrices are converted by CFCS, analysed with DEMATEL, and the factor weights drive a WISP ranking.
Source¶
examples/22_fuzzy_dematel.py
"""Fuzzy DEMATEL: three experts' triangular matrices, CFCS, causes and effects, then WISP.
Each expert states how strongly factor i influences factor j with a triangular fuzzy
number. `cfcs` (Opricovic and Tzeng 2003) converts the three matrices into one crisp
matrix; `dematel` then finds the total-relation matrix, the cause and effect groups
and factor weights. The factor weights (`as_weights()`) become the criteria weights
of a WISP ranking of three projects.
"""
from bulanik import TFN
from bulanik.mcdm import dematel, wisp
from bulanik.mcdm.dematel import cfcs
factors = ["quality", "cost", "delivery", "service"]
# linguistic influence words as triangular numbers
NO, LOW, MID, HIGH = TFN(0, 0, 0.25), TFN(0, 0.25, 0.5), TFN(0.25, 0.5, 0.75), TFN(0.5, 0.75, 1)
VERY = TFN(0.75, 1, 1)
expert_1 = [
[NO, MID, HIGH, HIGH],
[LOW, NO, MID, LOW],
[LOW, LOW, NO, MID],
[LOW, NO, LOW, NO],
]
expert_2 = [
[NO, HIGH, MID, HIGH],
[MID, NO, MID, LOW],
[LOW, MID, NO, HIGH],
[NO, LOW, LOW, NO],
]
expert_3 = [
[NO, MID, VERY, MID],
[LOW, NO, HIGH, NO],
[NO, LOW, NO, MID],
[LOW, LOW, NO, NO],
]
crisp = cfcs([expert_1, expert_2, expert_3])
result = dematel(crisp, factors)
print("total relation T (rounded):")
for name, row in zip(factors, result.total):
print(f" {name:<9}" + "".join(f"{x:8.3f}" for x in row))
print("prominence D+R:", {k: round(v, 3) for k, v in result.prominence.items()})
print("relation D-R :", {k: round(v, 3) for k, v in result.relation.items()})
print("causes:", ", ".join(result.causes))
print("effects:", ", ".join(result.effects))
weights = result.as_weights()
print("weights:", {k: round(v, 4) for k, v in weights.weights.items()})
projects = ["Project X", "Project Y", "Project Z"]
scores = [
[8, 6, 7, 5], # quality, cost (cost-type: lower is better), delivery, service
[7, 4, 8, 7],
[9, 7, 5, 6],
]
ranked = wisp(scores, weights, ["benefit", "cost", "benefit", "benefit"],
alternatives=projects, criteria=factors)
print("WISP ranking:", " > ".join(ranked.ranking))
assert abs(sum(weights.weights.values()) - 1.0) < 1e-9
assert result.causes == ("quality", "cost") and result.effects == ("delivery", "service")
expected_w = {"quality": 0.2877, "cost": 0.2332, "delivery": 0.2551, "service": 0.224}
assert all(abs(weights.weights[k] - v) < 5e-5 for k, v in expected_w.items())
assert ranked.ranking == ("Project Y", "Project X", "Project Z")
assert set(result.causes) | set(result.effects) == set(factors)
assert abs(sum(result.relation.values())) < 1e-9
# a TFN matrix handed to dematel directly is defuzzified by CFCS with one expert
single = dematel(expert_1, factors)
assert single.operators["defuzzification"] == "cfcs"
Output¶
Run when this page is built:
total relation T (rounded):
quality 0.234 0.540 0.678 0.661
cost 0.285 0.222 0.499 0.354
delivery 0.207 0.312 0.232 0.461
service 0.157 0.188 0.219 0.155
prominence D+R: {'quality': 2.995, 'cost': 2.622, 'delivery': 2.84, 'service': 2.35}
relation D-R : {'quality': 1.23, 'cost': 0.097, 'delivery': -0.415, 'service': -0.912}
causes: quality, cost
effects: delivery, service
weights: {'quality': 0.2877, 'cost': 0.2332, 'delivery': 0.2551, 'service': 0.224}
WISP ranking: Project Y > Project X > Project Z
What to notice¶
cfcstakes the minimum and maximum per cell across the experts, converts each expert, then averages.- A factor is a cause when its net relation D - R is positive, an effect otherwise.
result.as_weights()makes the DEMATEL weights usable by every ranking method.
Go further¶
- Guide: More weighting methods
- Guide: Ranking
- API:
bulanik.mcdm
Run it yourself: python examples/22_fuzzy_dematel.py.