Source code for skcriteria.agg.copras

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# License: BSD-3 (https://tldrlegal.com/license/bsd-3-clause-license-(revised))
# Copyright (c) 2016-2021, Cabral, Juan; Luczywo, Nadia
# Copyright (c) 2022-2025 QuatroPe
# All rights reserved.

# =============================================================================
# DOCS
# =============================================================================

"""COPRAS (Complex Proportional Assessment) method."""

# =============================================================================
# IMPORTS
# =============================================================================

from ..utils import hidden

with hidden():
    import numpy as np

    from ._agg_base import RankResult, SKCDecisionMakerABC
    from ..core import Objective
    from ..utils import doc_inherit, rank

# =============================================================================
# FUNCTIONS
# =============================================================================


[docs] def sum_indexes(matrix: np.ndarray, objectives: np.ndarray): """ Determine the sums of the minimizing and maximizing indexes. Each column represents a criterion. This function separates those to be maximized from those to be minimized, and sums the values accordingly for each alternative. """ criteria_max = np.compress( objectives == Objective.MAX.value, matrix, axis=1 ) criteria_min = np.compress( objectives == Objective.MIN.value, matrix, axis=1 ) s_max = np.sum(criteria_max, axis=1) s_min = np.sum(criteria_min, axis=1) return s_max, s_min
[docs] def determine_significances(s_max, s_min: np.ndarray): """ Determine the significances of the compared alternatives. This reflects the combined advantages and disadvantages of each alternative, using the COPRAS method formulas. """ min_s_min = np.min(s_min) dividend = min_s_min * np.sum(s_min) divisor_sum = np.sum(min_s_min / s_min) divisor = s_min * divisor_sum significances = s_max + (dividend / divisor) return significances
[docs] def copras(matrix, weights, objectives): """ Execute the COPRAS method without any validation. Steps: 1. Compute the weighted normalized decision-making matrix. 2. Calculate sums describing the alternatives. 3. Determine significances of alternatives. 4. Calculate the utility degree of each alternative. 5. Rank the alternatives based on utility. """ weighted_dm = matrix * weights s_max, s_min = sum_indexes(weighted_dm, objectives) significances = determine_significances(s_max, s_min) utility_degrees = significances / max(significances) * 100.0 ranking = rank.rank_values(utility_degrees, reverse=True) return ranking, utility_degrees, significances, s_max, s_min
# ============================================================================= # COPRAS # =============================================================================
[docs] class COPRAS(SKCDecisionMakerABC): """ The COPRAS method. The COmplex PRoportional ASsessment (COPRAS) method, introduced by Zavadskas and Kaklauskas, is used to evaluate the superiority of one alternative over another. It supports comparison of alternatives based on maximizing and minimizing index values. Raises ------ ValueError If any matrix value is < 0, if there are no criteria to minimize or if an alternative has all 0s for values in all minimizing criteria. References ---------- :cite:p:`zavadskas1996new` """ _skcriteria_parameters = [] @doc_inherit(SKCDecisionMakerABC._evaluate_data) def _evaluate_data(self, matrix, weights, objectives, **kwargs): if np.any(matrix < 0): raise ValueError("COPRAS cannot operate with values < 0") if not (Objective.MIN.value in objectives): raise ValueError( "COPRAS cannot operate solely on maximising criteria" ) sum_min = np.sum( matrix, axis=1, where=(Objective.MIN.value == objectives) ) if 0 in sum_min: raise ValueError( "COPRAS cannot operate when an alternative has all 0s" "for values in all minimizing criteria" ) ranking, score, significances, s_max, s_min = copras( matrix, weights, objectives ) return ranking, { "score": score, "significances": significances, "S_max": s_max, "S_min": s_min, } @doc_inherit(SKCDecisionMakerABC._make_result) def _make_result(self, alternatives, values, extra): return RankResult( "COPRAS", alternatives=alternatives, values=values, extra=extra )