Source code for etna.metrics.functional_metrics

from enum import Enum
from functools import partial
from typing import Optional
from typing import Sequence
from typing import Union

import numpy as np
from sklearn.metrics import mean_absolute_error as mae
from sklearn.metrics import mean_squared_error as mse
from sklearn.metrics import mean_squared_log_error as msle
from sklearn.metrics import median_absolute_error as medae
from sklearn.metrics import r2_score
from typing_extensions import assert_never

ArrayLike = Union[float, Sequence[float], Sequence[Sequence[float]]]


[docs]class FunctionalMetricMultioutput(str, Enum): """Enum for different functional metric multioutput modes.""" #: Compute one scalar value taking into account all outputs. joint = "joint" #: Compute one value per each output. raw_values = "raw_values" @classmethod def _missing_(cls, value): raise NotImplementedError( f"{value} is not a valid {cls.__name__}. Only {', '.join([repr(m.value) for m in cls])} options allowed" )
[docs]def _get_axis_by_multioutput(multioutput: str) -> Optional[int]: multioutput_enum = FunctionalMetricMultioutput(multioutput) if multioutput_enum is FunctionalMetricMultioutput.joint: return None elif multioutput_enum is FunctionalMetricMultioutput.raw_values: return 0 else: assert_never(multioutput_enum)
[docs]def mape(y_true: ArrayLike, y_pred: ArrayLike, eps: float = 1e-15, multioutput: str = "joint") -> ArrayLike: """Mean absolute percentage error. `Wikipedia entry on the Mean absolute percentage error <https://en.wikipedia.org/wiki/Mean_absolute_percentage_error>`_ Parameters ---------- y_true: array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred: array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. eps: MAPE is undefined for ``y_true[i]==0`` for any ``i``, so all zeros ``y_true[i]`` are clipped to ``max(eps, abs(y_true))``. multioutput: Defines aggregating of multiple output values (see :py:class:`~etna.metrics.functional_metrics.FunctionalMetricMultioutput`). Returns ------- : A non-negative floating point value (the best value is 0.0), or an array of floating point values, one for each individual target. """ y_true_array, y_pred_array = np.asarray(y_true), np.asarray(y_pred) if len(y_true_array.shape) != len(y_pred_array.shape): raise ValueError("Shapes of the labels must be the same") y_true_array = y_true_array.clip(eps) axis = _get_axis_by_multioutput(multioutput) return np.mean(np.abs((y_true_array - y_pred_array) / y_true_array), axis=axis) * 100
[docs]def smape(y_true: ArrayLike, y_pred: ArrayLike, eps: float = 1e-15, multioutput: str = "joint") -> ArrayLike: """Symmetric mean absolute percentage error. `Wikipedia entry on the Symmetric mean absolute percentage error <https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error>`_ .. math:: SMAPE = \dfrac{100}{n}\sum_{t=1}^{n}\dfrac{|ytrue_{t}-ypred_{t}|}{(|ypred_{t}|+|ytrue_{t}|) / 2} Parameters ---------- y_true: array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred: array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. eps: float=1e-15 SMAPE is undefined for ``y_true[i] + y_pred[i] == 0`` for any ``i``, so all zeros ``y_true[i] + y_pred[i]`` are clipped to ``max(eps, abs(y_true) + abs(y_pred))``. multioutput: Defines aggregating of multiple output values (see :py:class:`~etna.metrics.functional_metrics.FunctionalMetricMultioutput`). Returns ------- : A non-negative floating point value (the best value is 0.0), or an array of floating point values, one for each individual target. """ y_true_array, y_pred_array = np.asarray(y_true), np.asarray(y_pred) if len(y_true_array.shape) != len(y_pred_array.shape): raise ValueError("Shapes of the labels must be the same") axis = _get_axis_by_multioutput(multioutput) return 100 * np.mean( 2 * np.abs(y_pred_array - y_true_array) / (np.abs(y_true_array) + np.abs(y_pred_array)).clip(eps), axis=axis )
[docs]def sign(y_true: ArrayLike, y_pred: ArrayLike, multioutput: str = "joint") -> ArrayLike: """Sign error metric. .. math:: Sign(y\_true, y\_pred) = \\frac{1}{n}\\cdot\\sum_{i=0}^{n - 1}{sign(y\_true_i - y\_pred_i)} Parameters ---------- y_true: array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred: array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. multioutput: Defines aggregating of multiple output values (see :py:class:`~etna.metrics.functional_metrics.FunctionalMetricMultioutput`). Returns ------- : A floating point value, or an array of floating point values, one for each individual target. """ y_true_array, y_pred_array = np.asarray(y_true), np.asarray(y_pred) if len(y_true_array.shape) != len(y_pred_array.shape): raise ValueError("Shapes of the labels must be the same") axis = _get_axis_by_multioutput(multioutput) return np.mean(np.sign(y_true_array - y_pred_array), axis=axis)
[docs]def max_deviation(y_true: ArrayLike, y_pred: ArrayLike, multioutput: str = "joint") -> ArrayLike: """Max Deviation metric. Parameters ---------- y_true: array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred: array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. multioutput: Defines aggregating of multiple output values (see :py:class:`~etna.metrics.functional_metrics.FunctionalMetricMultioutput`). Returns ------- : A non-negative floating point value (the best value is 0.0), or an array of floating point values, one for each individual target. """ y_true_array, y_pred_array = np.asarray(y_true), np.asarray(y_pred) if len(y_true_array.shape) != len(y_pred_array.shape): raise ValueError("Shapes of the labels must be the same") axis = _get_axis_by_multioutput(multioutput) prefix_error_sum = np.cumsum(y_pred_array - y_true_array, axis=axis) return np.max(np.abs(prefix_error_sum), axis=axis)
rmse = partial(mse, squared=False)
[docs]def wape(y_true: ArrayLike, y_pred: ArrayLike, multioutput: str = "joint") -> ArrayLike: """Weighted average percentage Error metric. .. math:: WAPE(y\_true, y\_pred) = \\frac{\\sum_{i=0}^{n} |y\_true_i - y\_pred_i|}{\\sum_{i=0}^{n}|y\\_true_i|} Parameters ---------- y_true: array-like of shape (n_samples,) or (n_samples, n_outputs) Ground truth (correct) target values. y_pred: array-like of shape (n_samples,) or (n_samples, n_outputs) Estimated target values. multioutput: Defines aggregating of multiple output values (see :py:class:`~etna.metrics.functional_metrics.FunctionalMetricMultioutput`). Returns ------- : A non-negative floating point value (the best value is 0.0), or an array of floating point values, one for each individual target. """ y_true_array, y_pred_array = np.asarray(y_true), np.asarray(y_pred) if len(y_true_array.shape) != len(y_pred_array.shape): raise ValueError("Shapes of the labels must be the same") axis = _get_axis_by_multioutput(multioutput) return np.sum(np.abs(y_true_array - y_pred_array), axis=axis) / np.sum(np.abs(y_true_array), axis=axis) # type: ignore
__all__ = ["mae", "mse", "msle", "medae", "r2_score", "mape", "smape", "sign", "max_deviation", "rmse", "wape"]