Source code for skcriteria.validate

#!/usr/bin/env python
# -*- coding: utf-8 -*-

# Copyright (c) 2016-2017, Cabral, Juan; Luczywo, Nadia
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:

# * Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.

# * Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.

# * Neither the name of the copyright holder nor the names of its
# contributors may be used to endorse or promote products derived from
# this software without specific prior written permission.

# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

# =============================================================================
# FUTURE & DOCS
# =============================================================================

from __future__ import unicode_literals


__doc__ = """This module core functionalities for validate the data
used inside scikit criteria.

- Constants that represent minimization and mazimization criteria.
- Scikit-Criteria Criteria ndarray creation.
- Scikit-Criteria Data validation.

"""

__all__ = [
    'MIN', 'MAX',
    'DataValidationError',
    'criteriarr',
    'validate_data']


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

import itertools as it

import numpy as np


# =============================================================================
# CONSTANTS
# =============================================================================

MIN = -1
"""Int: Minimization criteria"""

MIN_ALIASES = [MIN, min, np.min, np.nanmin, np.amin, "min", "minimize"]
"""Another ways to name the minimization criterias."""

MAX = 1
"""Int: Maximization criteria"""

MAX_ALIASES = [MAX, max, np.max, np.nanmax, np.amax, "max", "maximize"]
"""Another way to name the maximization criterias."""


CRITERIA_STR = {
    MIN: "min",
    MAX: "max"
}

TABULATE_PARAMS = {
    "headers": "firstrow",
    "numalign": "center",
    "stralign": "center",
}


ALIASES = dict(it.chain(dict.fromkeys(MIN_ALIASES, MIN).items(),
                        dict.fromkeys(MAX_ALIASES, MAX).items()))


# =============================================================================
# EXCEPTIONS
# =============================================================================

[docs]class DataValidationError(ValueError): """Raised when some part of the multicriteria data (alternative matrix, criteria array or weights array) are not compatible with another part. """ pass
# ============================================================================= # FUNCTIONS # ============================================================================= def iter_equal(a, b): """Validate if two iterables are equals independently of their type.""" if isinstance(a, np.ndarray) or isinstance(b, np.ndarray): return np.allclose(a, b, equal_nan=True) return a == b def is_mtx(mtx, size=None): """Return True if mtx is two dimensional structure. If `size` is not None, must be a expected shape of the mtx """ try: mtx = np.asarray(mtx) a, b = mtx.shape if size and (a, b) != size: return False except Exception: return False return True
[docs]def criteriarr(criteria): """Validate if the iterable only contains MIN (or any alias) and MAX (or any alias) values. And also always returns an ndarray representation of the iterable. Parameters ---------- criteria : Array-like Iterable containing all the values to be validated by the function. Returns ------- numpy.ndarray : Criteria array. Raises ------ DataValidationError : if some value of the criteria array are not MIN (-1) or MAX (1) """ criteria = np.array([ALIASES.get(c, c) for c in criteria]) if np.setdiff1d(criteria, [MIN, MAX]).size > 0: msg = ( "Criteria Array only accept minimize or maximize Values. Found {}") raise DataValidationError(msg.format(MAX, MIN, criteria)) return criteria
[docs]def validate_data(mtx, criteria, weights=None): """Validate if the main components of the Data in scikit-criteria are compatible. The function tests: - The matrix (mtx) must be 2-dimensional. - The criteria array must be a criteria array (criteriarr function). - The number of criteria must be the same number of columns in mtx. - The weight array must be None or an iterable with the same length of the criteria. Parameters ---------- mtx : 2D array-like 2D alternative matrix, where every column (axis 0) are a criteria, and every row (axis 1) is an alternative. criteria : Array-like The sense of optimality of every criteria. Must has only MIN (-1) and MAX (1) values. Must has the same elements as columns has ``mtx`` weights : array like or None The importance of every criteria. Must has the same elements as columns has ``mtx`` or None. Returns ------- mtx : numpy.ndarray mtx representations as 2d numpy.ndarray. criteria : numpy.ndarray A criteria as numpy.ndarray. weights : numpy.ndarray or None A weights as numpy.ndarray or None (if weights is None). Raises ------ DataValidationError : If the data are incompatible. """ mtx = np.asarray(mtx) if not is_mtx(mtx): msg = "'mtx' must be a 2 dimensional array" raise DataValidationError(msg) criteria = criteriarr(criteria) if len(criteria) != np.shape(mtx)[1]: msg = "{} senses of optimality given but mtx has {} criteria".format( len(criteria), np.shape(mtx)[1]) raise DataValidationError(msg) weights = (np.asarray(weights) if weights is not None else None) if weights is not None: if len(weights) != len(criteria): msg = "{} weights given for {} criteria".format( len(weights), len(criteria)) raise DataValidationError(msg) return mtx, criteria, weights