Ir para o conteúdo

Documentation for Metrics

Common metrics to assess performance on NARX models.

explained_variance_score(y, yhat)

Calculate the Explained Variance Score.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

EVS output is non-negative values. Becoming 1.0 means your model outputs are exactly matched by true target values. Lower values means worse results.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> explained_variance_score(y, yhat)
0.957
Source code in sysidentpy/metrics/_regression.py
def explained_variance_score(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Explained Variance Score.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        EVS output is non-negative values. Becoming 1.0 means your
        model outputs are exactly matched by true target values.
        Lower values means worse results.

    References
    ----------
    - Wikipedia entry on the Explained Variance
       https://en.wikipedia.org/wiki/Explained_variation

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> explained_variance_score(y, yhat)
    0.957

    """
    xp = get_namespace(y, yhat)
    y_diff_avg = xp.mean(y - yhat)
    numerator = xp.mean((y - yhat - y_diff_avg) ** 2)
    y_avg = xp.mean(y)
    denominator = xp.mean((y - y_avg) ** 2)
    return float(_safe_variance_score(xp, numerator, denominator))

forecast_error(y, yhat)

Calculate the forecast error in a regression model.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss ndarray of floats

The difference between the true target values and the predicted or forecast value in regression or any other phenomenon.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> forecast_error(y, yhat)
[0.5, -0.5, 0, -1]
Source code in sysidentpy/metrics/_regression.py
def forecast_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the forecast error in a regression model.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : ndarray of floats
        The difference between the true target values and the predicted
        or forecast value in regression or any other phenomenon.

    References
    ----------
    - Wikipedia entry on the Forecast error
       https://en.wikipedia.org/wiki/Forecast_error

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> forecast_error(y, yhat)
    [0.5, -0.5, 0, -1]

    """
    return y - yhat

mean_absolute_error(y, yhat)

Calculate the Mean absolute error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float or ndarray of floats

MAE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> mean_absolute_error(y, yhat)
0.5
Source code in sysidentpy/metrics/_regression.py
def mean_absolute_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Mean absolute error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float or ndarray of floats
        MAE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

    References
    ----------
    - Wikipedia entry on the Mean absolute error
       https://en.wikipedia.org/wiki/Mean_absolute_error

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> mean_absolute_error(y, yhat)
    0.5

    """
    xp = get_namespace(y, yhat)
    return float(xp.mean(xp.abs(y - yhat)))

mean_absolute_scaled_error(y, yhat, y_train, seasonal_period=1)

Calculate the Mean Absolute Scaled Error.

MASE scales the mean absolute forecast error by the in-sample mean absolute error of a seasonal naive forecast.

Parameters:

Name Type Description Default
y array-like of shape (n_samples,) or (n_samples, 1)

True target values for the forecast horizon.

required
yhat array-like of shape (n_samples,) or (n_samples, 1)

Target values predicted by the model. yhat must have the same shape and temporal alignment as y.

required
y_train array-like of shape (n_training_samples,) or (n_training_samples, 1)

In-sample target values used to scale the forecast error.

required
seasonal_period int

Number of samples in one seasonal period. The default uses a one-step naive forecast as the scaling baseline.

1

Returns:

Name Type Description
loss float

Non-negative scaled error. Values below one indicate that the model's mean absolute error is lower than the in-sample seasonal-naive error. If the in-sample scale is zero, returns 0.0 for a perfect forecast and inf for an imperfect forecast.

Raises:

Type Description
TypeError

If seasonal_period is not an integer.

ValueError

If seasonal_period is not positive, the forecast arrays are empty or have incompatible shapes, the arrays are not single-output, or y_train does not contain more samples than seasonal_period.

Notes

The caller is responsible for aligning y and yhat after excluding model-specific initial conditions such as max_lag. This metric does not inspect the fitted model or alter the supplied arrays.

References

Examples:

>>> y_train = np.array([1.0, 2.0, 3.0, 4.0])
>>> y = np.array([5.0, 6.0])
>>> yhat = np.array([4.5, 5.5])
>>> mean_absolute_scaled_error(y, yhat, y_train)
0.5
Source code in sysidentpy/metrics/_regression.py
def mean_absolute_scaled_error(
    y: NDArray,
    yhat: NDArray,
    y_train: NDArray,
    seasonal_period: int = 1,
) -> float:
    r"""Calculate the Mean Absolute Scaled Error.

    MASE scales the mean absolute forecast error by the in-sample mean
    absolute error of a seasonal naive forecast.

    Parameters
    ----------
    y : array-like of shape (n_samples,) or (n_samples, 1)
        True target values for the forecast horizon.
    yhat : array-like of shape (n_samples,) or (n_samples, 1)
        Target values predicted by the model. ``yhat`` must have the same
        shape and temporal alignment as ``y``.
    y_train : array-like of shape (n_training_samples,) or (n_training_samples, 1)
        In-sample target values used to scale the forecast error.
    seasonal_period : int, default=1
        Number of samples in one seasonal period. The default uses a
        one-step naive forecast as the scaling baseline.

    Returns
    -------
    loss : float
        Non-negative scaled error. Values below one indicate that the model's
        mean absolute error is lower than the in-sample seasonal-naive error.
        If the in-sample scale is zero, returns 0.0 for a perfect forecast and
        ``inf`` for an imperfect forecast.

    Raises
    ------
    TypeError
        If ``seasonal_period`` is not an integer.
    ValueError
        If ``seasonal_period`` is not positive, the forecast arrays are empty
        or have incompatible shapes, the arrays are not single-output, or
        ``y_train`` does not contain more samples than ``seasonal_period``.

    Notes
    -----
    The caller is responsible for aligning ``y`` and ``yhat`` after excluding
    model-specific initial conditions such as ``max_lag``. This metric does not
    inspect the fitted model or alter the supplied arrays.

    References
    ----------
    - Hyndman, R. J., and Koehler, A. B. (2006). Another look at measures of
      forecast accuracy. International Journal of Forecasting, 22(4), 679-688.
      https://doi.org/10.1016/j.ijforecast.2006.03.001

    Examples
    --------
    >>> y_train = np.array([1.0, 2.0, 3.0, 4.0])
    >>> y = np.array([5.0, 6.0])
    >>> yhat = np.array([4.5, 5.5])
    >>> mean_absolute_scaled_error(y, yhat, y_train)
    0.5

    """
    if isinstance(seasonal_period, bool) or not isinstance(seasonal_period, Integral):
        raise TypeError(
            "seasonal_period must be an integer. "
            f"Got {type(seasonal_period).__name__}."
        )
    if seasonal_period <= 0:
        raise ValueError(
            "seasonal_period must be a positive integer. "
            f"Got {seasonal_period}."
        )

    xp = get_namespace(y, yhat, y_train)
    y = _as_float_array(xp, y)
    yhat = _as_float_array(xp, yhat)
    y_train = _as_float_array(xp, y_train)

    if y.shape != yhat.shape:
        raise ValueError(
            "y and yhat must have the same shape. "
            f"Got {y.shape} and {yhat.shape}."
        )
    if y.ndim == 0 or y.shape[0] == 0:
        raise ValueError("y and yhat must contain at least one sample.")
    if y.ndim > 2 or (y.ndim == 2 and y.shape[1] != 1):
        raise ValueError("y and yhat must contain a single output.")
    if y_train.ndim == 0 or y_train.shape[0] <= seasonal_period:
        raise ValueError(
            "y_train must contain more samples than seasonal_period. "
            f"Got {y_train.shape} and seasonal_period={seasonal_period}."
        )
    if y_train.ndim > 2 or (y_train.ndim == 2 and y_train.shape[1] != 1):
        raise ValueError("y_train must contain a single output.")

    forecast_error = xp.mean(xp.abs(y - yhat))
    naive_error = xp.mean(
        xp.abs(y_train[seasonal_period:] - y_train[:-seasonal_period])
    )
    if float(naive_error) == 0:
        forecast_error_value = float(forecast_error)
        if np.isnan(forecast_error_value):
            return float("nan")
        return 0.0 if forecast_error_value == 0 else float("inf")

    return float(forecast_error / naive_error)

mean_forecast_error(y, yhat)

Calculate the mean of forecast error of a regression model.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

The mean value of the difference between the true target values and the predicted or forecast value in regression or any other phenomenon.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> mean_forecast_error(y, yhat)
-0.25
Source code in sysidentpy/metrics/_regression.py
def mean_forecast_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the mean of forecast error of a regression model.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        The mean  value of the difference between the true target
        values and the predicted or forecast value in regression
        or any other phenomenon.

    References
    ----------
    - Wikipedia entry on the Forecast error
       https://en.wikipedia.org/wiki/Forecast_error

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> mean_forecast_error(y, yhat)
    -0.25

    """
    xp = get_namespace(y, yhat)
    return float(xp.mean(y - yhat))

mean_squared_error(y, yhat)

Calculate the Mean Squared Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

MSE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> mean_squared_error(y, yhat)
0.375
Source code in sysidentpy/metrics/_regression.py
def mean_squared_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Mean Squared Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        MSE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

    References
    ----------
    - Wikipedia entry on the Mean Squared Error
       https://en.wikipedia.org/wiki/Mean_squared_error

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> mean_squared_error(y, yhat)
    0.375

    """
    xp = get_namespace(y, yhat)
    return float(xp.mean((y - yhat) ** 2))

mean_squared_log_error(y, yhat)

Calculate the Mean Squared Logarithmic Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

MSLE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

Raises:

Type Description
ValueError

If y or yhat contains a value less than or equal to -1, where log1p is not defined for real-valued metrics.

Examples:

>>> y = [3, 5, 2.5, 7]
>>> yhat = [2.5, 5, 4, 8]
>>> mean_squared_log_error(y, yhat)
0.039
Source code in sysidentpy/metrics/_regression.py
def mean_squared_log_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Mean Squared Logarithmic Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        MSLE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

    Raises
    ------
    ValueError
        If ``y`` or ``yhat`` contains a value less than or equal to -1, where
        ``log1p`` is not defined for real-valued metrics.

    Examples
    --------
    >>> y = [3, 5, 2.5, 7]
    >>> yhat = [2.5, 5, 4, 8]
    >>> mean_squared_log_error(y, yhat)
    0.039

    """
    xp = get_namespace(y, yhat)
    if bool(xp.any(y <= -1)) or bool(xp.any(yhat <= -1)):
        raise ValueError(
            "Mean Squared Logarithmic Error cannot be used when targets "
            "contain values less than or equal to -1."
        )

    return mean_squared_error(xp.log(y + 1), xp.log(yhat + 1))

median_absolute_error(y, yhat)

Calculate the Median Absolute Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

MdAE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> median_absolute_error(y, yhat)
0.5
Source code in sysidentpy/metrics/_regression.py
def median_absolute_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Median Absolute Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        MdAE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

    References
    ----------
    - Wikipedia entry on the Median absolute deviation
       https://en.wikipedia.org/wiki/Median_absolute_deviation

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> median_absolute_error(y, yhat)
    0.5

    """
    xp = get_namespace(y, yhat)
    return float(_median(xp, xp.abs(y - yhat)))

normalized_root_mean_squared_error(y, yhat)

Calculate the normalized Root Mean Squared Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

nRMSE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

For a constant target, the normalization range is zero. In that case, this function returns 0.0 for a perfect prediction and inf for an imperfect prediction.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> normalized_root_mean_squared_error(y, yhat)
0.081
Source code in sysidentpy/metrics/_regression.py
def normalized_root_mean_squared_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the normalized Root Mean Squared Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        nRMSE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

        For a constant target, the normalization range is zero. In that case,
        this function returns 0.0 for a perfect prediction and ``inf`` for an
        imperfect prediction.

    References
    ----------
    - Wikipedia entry on the normalized Root Mean Squared Error
       https://en.wikipedia.org/wiki/Root-mean-square_deviation

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> normalized_root_mean_squared_error(y, yhat)
    0.081

    """
    xp = get_namespace(y, yhat)
    rmse = root_mean_squared_error(y, yhat)
    normalization_range = float(xp.max(y) - xp.min(y))
    if normalization_range == 0:
        if np.isnan(rmse):
            return float("nan")
        return 0.0 if rmse == 0 else float("inf")

    return float(rmse / normalization_range)

r2_score(y, yhat)

Calculate the R2 score. Based on sklearn solution.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

R2 output can be non-negative values or negative value. Becoming 1.0 means your model outputs are exactly matched by true target values. Lower values means worse results.

Notes

This is not a symmetric function.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> explained_variance_score(y, yhat)
0.948
Source code in sysidentpy/metrics/_regression.py
def r2_score(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the R2 score. Based on sklearn solution.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        R2 output can be non-negative values or negative value.
        Becoming 1.0 means your model outputs are exactly
        matched by true target values. Lower values means worse results.

    Notes
    -----
    This is not a symmetric function.

    References
    ----------
    - Wikipedia entry on the Coefficient of determination
       https://en.wikipedia.org/wiki/Coefficient_of_determination

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> explained_variance_score(y, yhat)
    0.948

    """
    xp = get_namespace(y, yhat)
    if _is_numpy_namespace(xp):
        numerator = ((y - yhat) ** 2).sum(axis=0, dtype=np.float64)
        denominator = ((y - np.average(y, axis=0)) ** 2).sum(axis=0, dtype=np.float64)
    else:
        numerator = xp.sum((y - yhat) ** 2, axis=0)
        denominator = xp.sum((y - xp.mean(y, axis=0)) ** 2, axis=0)
    output_scores = _safe_variance_score(xp, numerator, denominator)
    return float(xp.mean(output_scores))

root_mean_squared_error(y, yhat)

Calculate the Root Mean Squared Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

RMSE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> root_mean_squared_error(y, yhat)
0.612
Source code in sysidentpy/metrics/_regression.py
def root_mean_squared_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Root Mean Squared Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        RMSE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

    References
    ----------
    - Wikipedia entry on the Root Mean Squared Error
       https://en.wikipedia.org/wiki/Root-mean-square_deviation

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> root_mean_squared_error(y, yhat)
    0.612

    """
    return float(mean_squared_error(y, yhat) ** 0.5)

root_relative_squared_error(y, yhat)

Calculate the Root Relative Mean Squared Error.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

RRSE output is non-negative values. Becoming 0.0 means your model outputs are exactly matched by true target values.

For a constant target, the denominator is zero. In that case, this function returns 0.0 for a perfect prediction and inf for an imperfect prediction.

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> root_relative_mean_squared_error(y, yhat)
0.206
Source code in sysidentpy/metrics/_regression.py
def root_relative_squared_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the Root Relative Mean Squared Error.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        RRSE output is non-negative values. Becoming 0.0 means your
        model outputs are exactly matched by true target values.

        For a constant target, the denominator is zero. In that case, this
        function returns 0.0 for a perfect prediction and ``inf`` for an
        imperfect prediction.

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> root_relative_mean_squared_error(y, yhat)
    0.206

    """
    xp = get_namespace(y, yhat)
    numerator = xp.sum((yhat - y) ** 2)
    denominator = xp.sum((y - xp.mean(y, axis=0)) ** 2)
    denominator_value = float(denominator)
    if denominator_value == 0:
        numerator_value = float(numerator)
        if np.isnan(numerator_value):
            return float("nan")
        return 0.0 if numerator_value == 0 else float("inf")

    return float(xp.sqrt(numerator / denominator))

symmetric_mean_absolute_percentage_error(y, yhat)

Calculate the SMAPE score.

Parameters:

Name Type Description Default
y array-like of shape = number_of_outputs

Represent the target values.

required
yhat array-like of shape = number_of_outputs

Target values predicted by the model.

required

Returns:

Name Type Description
loss float

SMAPE output is a non-negative value. The results are percentages values.

Notes

One supposed problem with SMAPE is that it is not symmetric since over-forecasts and under-forecasts are not treated equally. When both the target and prediction are zero, their contribution is defined as zero.

References

Examples:

>>> y = [3, -0.5, 2, 7]
>>> yhat = [2.5, 0.0, 2, 8]
>>> symmetric_mean_absolute_percentage_error(y, yhat)
57.87
Source code in sysidentpy/metrics/_regression.py
def symmetric_mean_absolute_percentage_error(y: NDArray, yhat: NDArray) -> NDArray:
    """Calculate the SMAPE score.

    Parameters
    ----------
    y : array-like of shape = number_of_outputs
        Represent the target values.
    yhat : array-like of shape = number_of_outputs
        Target values predicted by the model.

    Returns
    -------
    loss : float
        SMAPE output is a non-negative value.
        The results are percentages values.

    Notes
    -----
    One supposed problem with SMAPE is that it is not symmetric since
    over-forecasts and under-forecasts are not treated equally.
    When both the target and prediction are zero, their contribution is
    defined as zero.

    References
    ----------
    - Wikipedia entry on the Symmetric mean absolute percentage error
       https://en.wikipedia.org/wiki/Symmetric_mean_absolute_percentage_error

    Examples
    --------
    >>> y = [3, -0.5, 2, 7]
    >>> yhat = [2.5, 0.0, 2, 8]
    >>> symmetric_mean_absolute_percentage_error(y, yhat)
    57.87

    """
    xp = get_namespace(y, yhat)
    n = y.shape[0]
    denominator = xp.abs(y) + xp.abs(yhat)
    nonzero_denominator = denominator != 0
    safe_denominator = xp.where(
        nonzero_denominator, denominator, xp.ones_like(denominator)
    )
    percentage_error = 2 * xp.abs(yhat - y) / safe_denominator
    percentage_error = xp.where(
        nonzero_denominator, percentage_error, xp.zeros_like(percentage_error)
    )
    return float(100 / n * xp.sum(percentage_error))