Skip to content

Commit

Permalink
refactor: delete config.py and add a function to validate dictionary …
Browse files Browse the repository at this point in the history
…keys in config settings
  • Loading branch information
Flaminietta committed Oct 21, 2024
1 parent 7742a1a commit 9d747ca
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 181 deletions.
143 changes: 0 additions & 143 deletions mcda/configuration/config.py

This file was deleted.

94 changes: 64 additions & 30 deletions mcda/configuration/configuration_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import numpy as np
import pandas as pd
from typing import Tuple, List, Union
from typing import Tuple, List, Union, Dict, Any

from mcda.utils.utils_for_main import pop_indexed_elements, check_norm_sum_weights, randomly_sample_all_weights, \
randomly_sample_ix_weight, check_input_matrix
Expand All @@ -14,10 +14,44 @@
logging.basicConfig(stream=sys.stdout, level=logging.DEBUG, format=FORMATTER)
logger = logging.getLogger("ProMCDA")

from typing import Dict, List, Any


def check_configuration_keys(sensitivity: dict, robustness: dict, monte_carlo: dict) -> bool:
"""
Checks for required keys in sensitivity, robustness, and monte_carlo dictionaries.
TODO: revisit this logic when substitute classes to handle configuration settings.
:param sensitivity : dict
:param robustness : dict
:param: monte_carlo : dict
:rtype: bool
"""

keys_of_dict_values = {
'sensitivity': ['sensitivity_on', 'normalization', 'aggregation'],
'robustness': ['robustness_on', 'on_single_weights', 'on_all_weights', 'given_weights', 'on_indicators'],
'monte_carlo': ['monte_carlo_runs', 'num_cores', 'random_seed', 'marginal_distribution_for_each_indicator']
}

_check_dict_keys(sensitivity, keys_of_dict_values['sensitivity'])
_check_dict_keys(robustness, keys_of_dict_values['robustness'])
_check_dict_keys(monte_carlo, keys_of_dict_values['monte_carlo'])

return True


def _check_dict_keys(dic: Dict[str, Any], expected_keys: List[str]) -> None:
"""
Helper function to check if the dictionary contains the required keys.
"""
for key in expected_keys:
if key not in dic:
raise KeyError(f"The key '{key}' is missing in the provided dictionary")


def extract_configuration_values(input_matrix: pd.DataFrame, polarity: Tuple[str], sensitivity: dict, robustness: dict,
monte_carlo: dict, output_path: str) -> dict:

"""
Extracts relevant configuration values required for running the ProMCDA process.
Expand Down Expand Up @@ -128,10 +162,10 @@ def check_configuration_values(extracted_values: dict) -> Tuple[int, int, List[s
# Check for sensitivity-related configuration errors
if sensitivity_on == "no":
check_config_error(normalization not in ['minmax', 'target', 'standardized', 'rank'],
'The available normalization functions are: minmax, target, standardized, rank.')
'The available normalization functions are: minmax, target, standardized, rank.')
check_config_error(aggregation not in ['weighted_sum', 'geometric', 'harmonic', 'minimum'],
'The available aggregation functions are: weighted_sum, geometric, harmonic, minimum.'
'\nWatch the correct spelling in the configuration.')
'The available aggregation functions are: weighted_sum, geometric, harmonic, minimum.'
'\nWatch the correct spelling in the configuration.')
logger.info("ProMCDA will only use one pair of norm/agg functions: " + normalization + '/' + aggregation)
else:
logger.info("ProMCDA will use a set of different pairs of norm/agg functions")
Expand All @@ -143,40 +177,40 @@ def check_configuration_values(extracted_values: dict) -> Tuple[int, int, List[s
check_config_error((robustness_on_single_weights == "no" and
robustness_on_all_weights == "no" and
robustness_on_indicators == "no"),
'Robustness analysis has been requested, but it’s unclear whether it should be applied to '
'weights or indicators. Please clarify it.')
'Robustness analysis has been requested, but it’s unclear whether it should be applied to '
'weights or indicators. Please clarify it.')

check_config_error((robustness_on_single_weights == "yes" and
robustness_on_all_weights == "yes" and
robustness_on_indicators == "no"),
'Robustness analysis has been requested for the weights, but it’s unclear whether it should '
'be applied to all weights or just one at a time? Please clarify.')
'Robustness analysis has been requested for the weights, but it’s unclear whether it should '
'be applied to all weights or just one at a time? Please clarify.')

check_config_error(((robustness_on_single_weights == "yes" and
robustness_on_all_weights == "yes" and
robustness_on_indicators == "yes") or
(robustness_on_single_weights == "yes" and
robustness_on_all_weights == "no" and
robustness_on_indicators == "yes") or
(robustness_on_single_weights == "no" and
robustness_on_all_weights == "yes" and
robustness_on_indicators == "yes")),
'Robustness analysis has been requested, but it’s unclear whether it should be applied to '
'weights or indicators. Please clarify.')
robustness_on_all_weights == "yes" and
robustness_on_indicators == "yes") or
(robustness_on_single_weights == "yes" and
robustness_on_all_weights == "no" and
robustness_on_indicators == "yes") or
(robustness_on_single_weights == "no" and
robustness_on_all_weights == "yes" and
robustness_on_indicators == "yes")),
'Robustness analysis has been requested, but it’s unclear whether it should be applied to '
'weights or indicators. Please clarify.')

# Check seetings for robustness analysis on weights or indicators
condition_robustness_on_weights = (
(robustness_on_single_weights == 'yes' and
robustness_on_all_weights == 'no' and
robustness_on_indicators == 'no') or
(robustness_on_single_weights == 'no' and
robustness_on_all_weights == 'yes' and
robustness_on_indicators == 'no'))
(robustness_on_single_weights == 'yes' and
robustness_on_all_weights == 'no' and
robustness_on_indicators == 'no') or
(robustness_on_single_weights == 'no' and
robustness_on_all_weights == 'yes' and
robustness_on_indicators == 'no'))

condition_robustness_on_indicators = (
(robustness_on_single_weights == 'no' and
robustness_on_all_weights == 'no' and
robustness_on_indicators == 'yes'))
robustness_on_all_weights == 'no' and
robustness_on_indicators == 'yes'))

is_robustness_weights, is_robustness_indicators = check_config_setting(condition_robustness_on_weights,
condition_robustness_on_indicators,
Expand All @@ -194,7 +228,8 @@ def check_configuration_values(extracted_values: dict) -> Tuple[int, int, List[s
num_indicators = (input_matrix_no_alternatives.shape[1] - num_non_exact_and_non_poisson)

# Process indicators and weights based on input parameters in the configuration
polar, weights = process_indicators_and_weights(extracted_values, input_matrix_no_alternatives, is_robustness_indicators,
polar, weights = process_indicators_and_weights(extracted_values, input_matrix_no_alternatives,
is_robustness_indicators,
is_robustness_weights, polarity, monte_carlo_runs, num_indicators)

# Check the number of indicators, weights, and polarities
Expand Down Expand Up @@ -342,7 +377,7 @@ def _handle_polarities_and_weights(is_robustness_indicators: int, is_robustness_
col_to_drop_indexes: np.ndarray, polar: List[str], config: dict, mc_runs: int,
num_indicators: int) \
-> Union[Tuple[List[str], list, None, None], Tuple[List[str], None, List[List], None],
Tuple[List[str], None, None, dict]]:
Tuple[List[str], None, None, dict]]:
"""
Manage polarities and weights based on the specified robustness settings, ensuring that the appropriate adjustments
and normalizations are applied before returning the necessary data structures.
Expand Down Expand Up @@ -455,4 +490,3 @@ def check_indicator_weights_polarities(num_indicators: int, polar: List[str], co
if (config["robustness_on_all_weights"] == "no") and (
num_indicators != len(config["given_weights"])):
raise ValueError('The no. of fixed weights does not correspond to the no. of indicators')

25 changes: 17 additions & 8 deletions mcda/models/ProMCDA.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
import pandas as pd
from typing import Tuple, List, Union

from mcda.configuration.configuration_validator import extract_configuration_values, check_configuration_values
from mcda.configuration.configuration_validator import extract_configuration_values, check_configuration_values, \
check_configuration_keys
from mcda.utils.utils_for_main import run_mcda_without_indicator_uncertainty, run_mcda_with_indicator_uncertainty

log = logging.getLogger(__name__)
Expand Down Expand Up @@ -43,8 +44,14 @@ def __init__(self, input_matrix: pd.DataFrame, polarity: Tuple[str, ...], sensit
self.monte_carlo = monte_carlo
self.output_path = output_path

# self.validate_input_parameters_keys # TODO: still need a formal check as made in old config class,
# maybe use some of following functions validate_
# Check configuration dictionary keys and handle potential issues
# TODO: revisit this logic when substitute classes to handle configuration settings
try:
check_configuration_keys(self.sensitivity, self.robustness, self.monte_carlo)
except KeyError as e:
print(f"Configuration Error: {e}")
raise # Optionally re-raise the error after logging it

is_robustness_indicators, is_robustness_weights, polar, weights, configuration_settings = self.validate_inputs()
self.run_mcda(is_robustness_indicators, is_robustness_weights, weights, configuration_settings)

Expand All @@ -57,18 +64,20 @@ def validate_inputs(self) -> Tuple[int, int, list, Union[list, List[list], dict]
Extract and validate input configuration parameters to ensure they are correct.
Return a flag indicating whether robustness analysis will be performed on indicators (1) or not (0).
"""

configuration_values = extract_configuration_values(self.input_matrix, self.polarity, self.sensitivity,
self.robustness, self.monte_carlo, self.output_path)
is_robustness_indicators, is_robustness_weights, polar, weights = check_configuration_values(
configuration_values)

# Validate input TODO: move into a different function validate_input_parameters_keys
# self.validate_normalization(self.sensitivity['normalization'])
# self.validate_aggregation(self.sensitivity['aggregation'])
# self.validate_robustness(self.robustness)

return is_robustness_indicators, is_robustness_weights, polar, weights, configuration_values



# self.validate_normalization(self.sensitivity['normalization'])
# self.validate_aggregation(self.sensitivity['aggregation'])
# self.validate_robustness(self.robustness)

# def validate_normalization(self, f_norm):
# """
# Validate the normalization method.
Expand Down

0 comments on commit 9d747ca

Please sign in to comment.