Source code for lsapy.functions._suitability
"""Suitability Function Module."""
from __future__ import annotations
import warnings
from collections.abc import Callable
from typing import Any
import matplotlib.pyplot as plt
import numpy as np
from lsapy.core.functions import get_function_from_name
__all__ = [
"SuitabilityFunction",
]
[docs]
class SuitabilityFunction:
"""
Suitability Function.
Suitability function define how the criteria indicator is transformed into a suitability value. The suitability
function are available for continuous and discrete indicators. For continuous indicators, a membership function
is applied to convert indicator values into a suitability. For discrete indicators, a set of rules is mapped
on the indicator.
Parameters
----------
func : Callable | None, optional
Function to compute the suitability value.
name : str | None, optional
Name of the implemented function to use. If `func` is provided, this parameter is ignored.
params : dict[str, Any], optional
Parameters of the function.
Examples
--------
>>> func = SuitabilityFunction(name="logistic", params={"a": 1, "b": 5})
>>> func(3)
array(0.11920292, dtype=float32)
``SuitabilityFunction`` can also be used for discrete functions.
>>> func = SuitabilityFunction(name="discrete", params={"rules": {1: 0, 2: 0.1, 3: 0.5, 4: 0.9, 5: 1}})
>>> func(3)
array(0.5, dtype=float32)
"""
[docs]
def __init__(self, func: Callable | None = None, name: str | None = None, params: dict[str, Any] | None = None):
if func is None:
if name is None:
raise ValueError("Either `func` or `name` must be provided to define the suitability function.")
elif not isinstance(name, str):
raise TypeError("`name` must be a string when `func` is not provided.")
else:
self.func = get_function_from_name(name)
else:
if not isinstance(func, Callable):
raise TypeError("`func` must be a callable function.")
if name is not None:
warnings.warn("`name` is ignored when `func` is provided", stacklevel=2)
self.func = func
self.params = params
def __call__(self, x):
"""Call the suitability function."""
if self.func is None:
raise ValueError("No function has been provided.")
if self.params:
def func(val):
"""Wrap the function to include parameters."""
return self.func(val, **self.params)
else:
def func(val):
"""Wrap the function without parameters."""
return self.func(val)
return np.vectorize(func, otypes=[np.float32])(x)
def __repr__(self):
"""Return the string representation of the object."""
return f"{self.__class__.__name__}(func={self.func.__name__}, params={self.params})"
@property
def attrs(self) -> dict[str, Any]:
"""
Dictionary of the suitability function attributes.
Returns
-------
dict
Dictionary containing the function name and parameters. If both are undefined, an empty dictionary
is returned.
"""
return {k: v for k, v in {"func": self.func, "params": self.params}.items() if v is not None}
def map(self, x):
"""
Map the suitability function.
This method converts the input values into suitability values for the defined function.
Parameters
----------
x : any
Input values to map.
Returns
-------
any
Suitability values.
Raises
------
ValueError
If no function has been provided.
Examples
--------
>>> from lsapy.functions import logistic
>>> sf = SuitabilityFunction(func=logistic, params={"a": 1, "b": 5})
>>> sf.map(3)
array(0.11920292, dtype=float32)
.. deprecated:: 0.1.0-dev2
`map` will be removed in LSAPy 0.1.0 because it is redundant with the `__call__` method.
Please use the `__call__` method directly instead.
"""
warnings.warn(
"`map` is deprecated and will be removed in LSAPy 0.1.0. Use `__call__` directly instead.",
FutureWarning,
stacklevel=2,
)
return self(x)
def plot(self, x) -> None:
"""
Basic plot of the suitability function.
Parameters
----------
x : any
Input values to plot.
Examples
--------
>>> import numpy as np # doctest: +SKIP
>>> from lsapy.functions import logistic
>>> sf = SuitabilityFunction(func=logistic, params={"a": 1, "b": 5})
>>> sf.plot(np.linspace(0, 10, 100)) # doctest: +SKIP
"""
plt.plot(x, self(x))