Criteria

Relevance identification

pysensmcda.criteria.identification.relevance_identification(method: callable, call_kwargs: dict, ranking_descending: bool, excluded_criteria: int = 1, return_all: bool = False, corr_coef: ~typing.Callable | ~typing.List[~typing.Callable] = <function weighted_spearman>, precision: int = 6) list[tuple[tuple[int] | int, tuple[float] | float, float, ndarray]][source]

The core idea behind this method is to iteratively exclude a specified number of criteria and evaluate the impact on the ranking of alternatives. By systematically excluding different criteria and analyzing the resulting changes in the ranking, the function aims to identify which criteria significantly influence the final decision.

Parameters:

methodcallable

The evaluation function to be used for preference and ranking calculation. Should include matrix, weights, and types as one of the parameters to pass modified input data to the assessment process.

call_kwargsdict

Dictionary with keyword arguments to be passed to the evaluation function. Should include matrix, weights, and types as one of the parameters to pass modified input data to the assessment process.

ranking_descending: bool

Flag determining the direction of alternatives ordering in ranking. By setting the flag to True, greater values will have better positions in the ranking.

excluded_criteria: int, optional, default=1

Number of criteria to be excluded in each iteration.

return_all: bool, optional, default=False

Flag determining if the returned results include all data in criteria identification process If True, all data is return for each iteration of relevance identification. If False, only the least relevant criterion for each iteration is returned.

corr_coef: callable | list, optional, default=pymcdm.correlations.weighted_spearman

Function which will be used to check similarity of rankings while achieving compromise. If callable, then correlation calculated for given coefficient. If list of callables, then correlation calculated for multiple coefficients.

precision: int, optional, default=6

Precision for rounding the results.

Returns:

list

List of tuples containing information about the relevance identification process. Each tuple includes the excluded criteria indices, correlation coefficient values, distance calculated as the sum of the Euclidean distance between preferences, and the modified matrix.

Examples:

Example 1: Identify relevant criteria in a custom matrix using TOPSIS method

>>> matrix = np.array([
...    [4, 3, 5, 7],
...    [7, 4, 2, 4],
...    [9, 5, 7, 3],
...    [3, 5, 6, 3]
... ])
>>> criteria_types = np.array([1, 1, -1, 1])
>>> weights = equal_weights(matrix)
>>> topsis = pm.TOPSIS(normalization_function=norm.vector_normalization)
>>> call_kwargs = {
...     'matrix': matrix,
...     'weights': weights,
...     'types': criteria_types
... }
>>> results = relevance_identification(topsis, call_kwargs, ranking_descending=True)
>>> for r in results:
...     print(r)

Example 2: Identify relevant criteria in using TOPSIS method and excluding 3 criteria in the problem

>>> matrix = np.array([
...    [106.78,  6.75,  2.  , 220.  ,  6.  ,  1.  , 52.  , 455.5 ,  8.9 , 36.8 ],
...    [ 86.37,  7.12,  3.  , 400.  , 10.  ,  0.  , 20.  , 336.5 ,  7.2 , 29.8 ],
...    [104.85,  6.95, 60.  , 220.  ,  7.  ,  1.  , 60.  , 416.  ,  8.7 , 36.2 ],
...    [ 46.6 ,  6.04,  1.  , 220.  ,  3.  ,  0.  , 50.  , 277.  ,  3.9 , 16.  ],
...    [ 69.18,  7.05, 33.16, 220.  ,  8.  ,  0.  , 35.49, 364.79,  5.39, 33.71],
...    [ 66.48,  6.06, 26.32, 220.  ,  6.53,  0.  , 34.82, 304.02,  4.67, 27.07],
...    [ 74.48,  6.61, 48.25, 400.  ,  4.76,  1.  , 44.19, 349.45,  4.93, 28.89],
...    [ 73.67,  6.06, 19.54, 400.  ,  3.19,  0.  , 46.41, 354.65,  8.01, 21.09],
...    [100.58,  6.37, 39.27, 220.  ,  8.43,  1.  , 22.07, 449.42,  7.89, 17.62],
...    [ 94.81,  6.13, 50.58, 220.  ,  4.18,  1.  , 21.14, 450.88,  5.12, 17.3 ],
...    [ 48.93,  7.12, 21.48, 220.  ,  5.47,  1.  , 55.72, 454.71,  8.39, 19.16],
...    [ 74.75,  6.58,  7.08, 400.  ,  9.9 ,  1.  , 26.01, 455.17,  4.78, 18.44]
... ])
>>> criteria_types = np.array([1, 1, -1, 1, -1, -1, 1, -1, -1, 1])
>>> weights = equal_weights(matrix)
>>> topsis = pm.TOPSIS(normalization_function=norm.vector_normalization)
>>> call_kwargs = {
...     'matrix': matrix,
...     'weights': weights,
...     'types': criteria_types
... }
>>> results = relevance_identification(topsis, call_kwargs, corr_coef=[pymcdm.correlations.rw, pymcdm.correlations.ws, pymcdm.correlations.rs], ranking_descending=True, excluded_criteria=3)
>>> for r in results:
...     print(r)

Example 3: Identify relevant criteria in using TOPSIS method and excluding 3 criteria in the problem, and return all partial results for identification process

>>> matrix = np.array([
...    [106.78,  6.75,  2.  , 220.  ,  6.  ,  1.  , 52.  , 455.5 ,  8.9 , 36.8 ],
...    [ 86.37,  7.12,  3.  , 400.  , 10.  ,  0.  , 20.  , 336.5 ,  7.2 , 29.8 ],
...    [104.85,  6.95, 60.  , 220.  ,  7.  ,  1.  , 60.  , 416.  ,  8.7 , 36.2 ],
...    [ 46.6 ,  6.04,  1.  , 220.  ,  3.  ,  0.  , 50.  , 277.  ,  3.9 , 16.  ],
...    [ 69.18,  7.05, 33.16, 220.  ,  8.  ,  0.  , 35.49, 364.79,  5.39, 33.71],
...    [ 66.48,  6.06, 26.32, 220.  ,  6.53,  0.  , 34.82, 304.02,  4.67, 27.07],
...    [ 74.48,  6.61, 48.25, 400.  ,  4.76,  1.  , 44.19, 349.45,  4.93, 28.89],
...    [ 73.67,  6.06, 19.54, 400.  ,  3.19,  0.  , 46.41, 354.65,  8.01, 21.09],
...    [100.58,  6.37, 39.27, 220.  ,  8.43,  1.  , 22.07, 449.42,  7.89, 17.62],
...    [ 94.81,  6.13, 50.58, 220.  ,  4.18,  1.  , 21.14, 450.88,  5.12, 17.3 ],
...    [ 48.93,  7.12, 21.48, 220.  ,  5.47,  1.  , 55.72, 454.71,  8.39, 19.16],
...    [ 74.75,  6.58,  7.08, 400.  ,  9.9 ,  1.  , 26.01, 455.17,  4.78, 18.44]
... ])
>>> criteria_types = np.array([1, 1, -1, 1, -1, -1, 1, -1, -1, 1])
>>> weights = equal_weights(matrix)
>>> topsis = pm.TOPSIS(normalization_function=norm.vector_normalization)
>>> call_kwargs = {
...     'matrix': matrix,
...     'weights': weights,
...     'types': criteria_types
... }
>>> results = relevance_identification(topsis, call_kwargs, corr_coef=[pymcdm.correlations.rw, pymcdm.correlations.ws, pymcdm.correlations.rs], ranking_descending=True, excluded_criteria=3, return_all=True)
>>> for result in results:
>>>     for res in result:
...         print(r)

Percentage modification

pysensmcda.criteria.percentage.percentage_modification(weights: ndarray, percentages: int | ndarray, direction: None | ndarray = None, indexes: None | ndarray = None, step: int | float = 1) list[tuple[int | tuple[int], tuple[float], ndarray]][source]

Modify a set of criteria weights based on specified percentage changes, directions, and indexes.

Parameters:

weightsndarray

1D array representing the initial criteria weights. Should sum up to 1.

percentagesint | ndarray

Percentage changes to be applied to the criteria weights. If int, the same percentage change is applied to all criteria. If ndarray, it specifies the percentage change for each criterion individually.

directionNone | ndarray, optional, default=None

Direction of the modification for each criterion. If None, both increase and decrease directions are considered. If ndarray, it specifies the direction (1 for increase, -1 for decrease) for each criterion individually.

indexesNone | ndarray, optional, default=None

Indexes of the criteria to be modified. If None, all criteria are considered subsequently. If ndarray, it specifies the indexes or combinations of indexes for the criteria to be modified.

stepint | float, optional, default=1

Step size for the percentage change.

Returns:

List[Tuple[int | tuple, tuple, ndarray]]

A list of tuples containing information about the modified criteria index, percentage change, and the resulting criteria weights.

Examples:

Example 1: Modify weights with a single percentage change

>>> weights = np.array([0.3, 0.3, 0.4])
>>> percentage = 5
>>> results = percentage_modification(weights, percentage)
>>> for r in results:
...     print(r)

Example 2: Modify weights with percentages, specific indexes, and step size

>>> weights = np.array([0.3, 0.3, 0.4])
>>> percentages = np.array([5, 5, 5])
>>> indexes = np.array([[0, 1], 2], dtype='object')
>>> results = percentage_modification(weights, percentages, indexes=indexes)
>>> for r in results:
...     print(r)

Example 3: Modify weights with percentages and specific direction for each criterion

>>> weights = np.array([0.3, 0.3, 0.4])
>>> percentages = np.array([6, 4, 5])
>>> direction = np.array([-1, 1, -1])
>>> results = percentage_modification(weights, percentages, direction=direction)
>>> for r in results:
...     print(r)

Example 4: Modify weights with percentages, specific indexes, and individual step sizes

>>> weights = np.array([0.3, 0.3, 0.4])
>>> percentages = np.array([6, 4, 8])
>>> indexes = np.array([0, 2])
>>> step = 2
>>> results = percentage_modification(weights, percentages, indexes=indexes, step=step)
>>> for r in results:
...     print(r)

Range modification

pysensmcda.criteria.range.range_modification(weights: ndarray, range_values: ndarray, indexes: None | ndarray = None, step: float = 0.01) list[tuple[int, float | tuple[float], ndarray]][source]

Modify a set of criteria weights based on specified range values, directions, and indexes.

Parameters:

weightsndarray

1D array representing the initial criteria weights. Should sum up to 1.

range_valuesndarray

Range of values for each criterion specifying the allowed changes. Should be given as a two-dimensional array where each row represents a criterion, and the columns represent the lower and upper bounds of the allowed range.

indexesNone | ndarray, optional, default=None

Indexes of the criteria to be modified. If None, all criteria are considered subsequently. If ndarray, it specifies the indexes or combinations of indexes for the criteria to be modified.

stepfloat, optional, default=0.01

Step size for the change in given range.

Returns:

List[Tuple[int, Union[float, Tuple[float, …]], ndarray]]

A list of tuples containing information about the modified criteria index, range change, and the resulting criteria weights.

Examples:

Example 1: Modify weights with a single range change

>>> weights = np.array([0.3, 0.3, 0.4])
>>> range_values = np.array([[0.25, 0.3], [0.3, 0.35], [0.37, 0.43]])
>>> results = range_modification(weights, range_values)
>>> for r in results:
...     print(r)

Example 2: Modify weights with range values, specific indexes, and step size

>>> weights = np.array([0.3, 0.3, 0.4])
>>> indexes = np.array([[0, 1], 2], dtype='object')
>>> range_values = np.array([[0.25, 0.3], [0.3, 0.35], [0.37, 0.43]])
>>> results = range_modification(weights, range_values, indexes=indexes)
>>> for r in results:
...     print(r)

Example 3: Modify weights with range values and individual step sizes

>>> weights = np.array([0.3, 0.3, 0.4])
>>> range_values = np.array([[0.25, 0.3], [0.3, 0.35], [0.37, 0.43]])
>>> step = 0.02
>>> results = range_modification(weights, range_values, step=step)
>>> for r in results:
...     print(r)

Criteria removal

pysensmcda.criteria.removal.remove_criteria(matrix: ndarray, weights: ndarray, indexes: None | int | ndarray = None) list[tuple[int, ndarray, ndarray]][source]

Remove one or more criteria from a decision matrix and adjust corresponding criteria weights.

Parameters:

matrixndarray

2D array with decision matrix containing multiple criteria and alternatives.

weightsndarray

1D vector of initial criteria weights.

indexesNone | int | ndarray, optional, default=None

Index or array of indexes specifying which criteria to remove. If None, one criterion will be removed by default

Returns:

List[Tuple[int, ndarray, ndarray]]

A list of tuples containing information about the removed criteria, new decision matrix, and adjusted criteria weights.

Examples:

Example 1: no indexes given

>>> matrix = np.array([
...     [1, 2, 3, 4, 4],
...     [1, 2, 3, 4, 4],
...     [4, 3, 2, 1, 4]
... ])
>>> weights = np.array([0.25, 0.25, 0.2, 0.2, 0.1])
>>> results = remove_criteria(matrix, weights)
>>> for result in results:
...     print(result)

Example 2: int index given

>>> matrix = np.array([
...     [1, 2, 3, 4, 4],
...     [1, 2, 3, 4, 4],
...     [4, 3, 2, 1, 4]
... ])
>>> weights = np.array([0.25, 0.25, 0.2, 0.2, 0.1])
>>> results = remove_criteria(matrix, weights, 3)
>>> for result in results:
...     print(result)

Example 3: array indexes given, one-dimensional

>>> matrix = np.array([
...     [1, 2, 3, 4, 4],
...     [1, 2, 3, 4, 4],
...     [4, 3, 2, 1, 4]
... ])
>>> weights = np.array([0.25, 0.25, 0.2, 0.2, 0.1])
>>> results = remove_criteria(matrix, weights, np.array([1, 2, 3]))
>>> for result in results:
...     print(result)

Example 4: array indexes given, elements of array as list

>>> matrix = np.array([
...     [1, 2, 3, 4, 4],
...     [1, 2, 3, 4, 4],
...     [4, 3, 2, 1, 4]
... ])
>>> weights = np.array([0.25, 0.25, 0.2, 0.2, 0.1])
>>> results = remove_criteria(matrix, weights, np.array([[0, 4], 2, 3], dtype='object'))
>>> for result in results:
...     print(result)

Weights scenarios

pysensmcda.criteria.scenarios.generate_weights_scenarios(crit_num: int, step: float, precision: int = 4, cores_num: int | None = None, file_name: str | None = None, return_array: bool = False, sequential: bool = False, save_zeros: bool = True) list | None[source]

Generate scenarios for examining criteria weights based on given criteria number and step of weights space exploration

Parameters:

crit_numint

The number of criteria.

stepfloat

The step size used for generating criteria weights.

precisionint, optional, default=4

The number of decimal places to round the generated criteria weights.

cores_numint or None, optional, default=None

If provided, the generated scenarios will be generated with given number of processes. If None, all available CPU cores will be used.

filenamestr or None, optional, default=None or ‘out’

Using parallel version files are always created. Temporary for subsequent processes and main file that contains all results. Temporary files are deleted after completion. If provided, the generated scenarios will be saved to the specified file. If None, scenarios will be returned as a list.

return_arraybool, optional, default=False

Returns results in a format of nd.array (numpy)

sequential: bool, optional, default=False

If True code will be run sequentially. Progressbar will be shown and non temporary files created.

save_zeros: bool, optional, default=True

If True saves weights vectors where zeros are present.

Returns:

list or None

Depending on return_array parameter, None or nd.array will be returned.

Examples:

Example 1: parallel without array return

>>> generate_weights_scenarios(4, 0.1, 3)
>>> # results will be saved to 'out.npy'

Example 2: parallel with array return

>>> scenarios = generate_weights_scenarios(4, 0.1, 3, return_array=True)
>>> print(scenarios)
>>> [(0.9, 0.1, 0.0, 0.0), (0.8, 0.2, 0.0, 0.0), ...]
>>> # results will be saved to 'out.npy'

Example 3: parallel with custom file name

>>> generate_weights_scenarios(4, 0.1, 3, file_name='4crit_0.1')
>>> # results will be saved to '4crit_0.1.npy'

Example 3: sequential

>>> generate_weights_scenarios(4, 0.1, 3, sequential=True)
>>> print(scenarios)
>>> [(0.9, 0.1, 0.0, 0.0), (0.8, 0.2, 0.0, 0.0), ...]
>>> # results will not be saved

Example 4: sequential with saving to file

>>> generate_weights_scenarios(4, 0.1, 3, sequential=True, file_name='4crit_0.1')
>>> print(scenarios)
>>> [(0.9, 0.1, 0.0, 0.0), (0.8, 0.2, 0.0, 0.0), ...]
>>> # results will be saved to '4crit_0.1.npy'