Source code for spacetimeformer.eval_stats

import numpy as np

"""
Credits to @bshishov

https://gist.github.com/bshishov/5dc237f59f019b26145648e2124ca1c9
"""

EPSILON = 1e-10


def _error(actual: np.ndarray, predicted: np.ndarray):
    """Simple error"""
    return actual - predicted


def _percentage_error(actual: np.ndarray, predicted: np.ndarray):
    """
    Percentage error

    Note: result is NOT multiplied by 100
    """
    return _error(actual, predicted) / (actual + EPSILON)


def _naive_forecasting(actual: np.ndarray, seasonality: int = 1):
    """Naive forecasting method which just repeats previous samples"""
    return actual[:-seasonality]


def _relative_error(
    actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None
):
    """Relative Error"""
    if benchmark is None or isinstance(benchmark, int):
        # If no benchmark prediction provided - use naive forecasting
        if not isinstance(benchmark, int):
            seasonality = 1
        else:
            seasonality = benchmark
        return _error(actual[seasonality:], predicted[seasonality:]) / (
            _error(actual[seasonality:], _naive_forecasting(actual, seasonality))
            + EPSILON
        )

    return _error(actual, predicted) / (_error(actual, benchmark) + EPSILON)


def _bounded_relative_error(
    actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None
):
    """Bounded Relative Error"""
    if benchmark is None or isinstance(benchmark, int):
        # If no benchmark prediction provided - use naive forecasting
        if not isinstance(benchmark, int):
            seasonality = 1
        else:
            seasonality = benchmark

        abs_err = np.abs(_error(actual[seasonality:], predicted[seasonality:]))
        abs_err_bench = np.abs(
            _error(actual[seasonality:], _naive_forecasting(actual, seasonality))
        )
    else:
        abs_err = np.abs(_error(actual, predicted))
        abs_err_bench = np.abs(_error(actual, benchmark))

    return abs_err / (abs_err + abs_err_bench + EPSILON)


def _geometric_mean(a, axis=0, dtype=None):
    """Geometric mean"""
    if not isinstance(a, np.ndarray):  # if not an ndarray object attempt to convert it
        log_a = np.log(np.array(a, dtype=dtype))
    elif dtype:  # Must change the default dtype allowing array type
        if isinstance(a, np.ma.MaskedArray):
            log_a = np.log(np.ma.asarray(a, dtype=dtype))
        else:
            log_a = np.log(np.asarray(a, dtype=dtype))
    else:
        log_a = np.log(a)
    return np.exp(log_a.mean(axis=axis))


[docs]def mse(actual: np.ndarray, predicted: np.ndarray): """Mean Squared Error""" return np.mean(np.square(_error(actual, predicted)))
[docs]def rmse(actual: np.ndarray, predicted: np.ndarray): """Root Mean Squared Error""" return np.sqrt(mse(actual, predicted))
[docs]def nrmse(actual: np.ndarray, predicted: np.ndarray): """Normalized Root Mean Squared Error""" return rmse(actual, predicted) / (actual.max() - actual.min())
[docs]def me(actual: np.ndarray, predicted: np.ndarray): """Mean Error""" return np.mean(_error(actual, predicted))
[docs]def mae(actual: np.ndarray, predicted: np.ndarray): """Mean Absolute Error""" return np.mean(np.abs(_error(actual, predicted)))
mad = mae # Mean Absolute Deviation (it is the same as MAE)
[docs]def gmae(actual: np.ndarray, predicted: np.ndarray): """Geometric Mean Absolute Error""" return _geometric_mean(np.abs(_error(actual, predicted)))
[docs]def mdae(actual: np.ndarray, predicted: np.ndarray): """Median Absolute Error""" return np.median(np.abs(_error(actual, predicted)))
[docs]def mpe(actual: np.ndarray, predicted: np.ndarray): """Mean Percentage Error""" return np.mean(_percentage_error(actual, predicted))
[docs]def mape(actual: np.ndarray, predicted: np.ndarray): """ Mean Absolute Percentage Error Properties: + Easy to interpret + Scale independent - Biased, not symmetric - Undefined when actual[t] == 0 Note: result is NOT multiplied by 100 """ return np.mean(np.abs(_percentage_error(actual, predicted)))
[docs]def mdape(actual: np.ndarray, predicted: np.ndarray): """ Median Absolute Percentage Error Note: result is NOT multiplied by 100 """ return np.median(np.abs(_percentage_error(actual, predicted)))
[docs]def smape(actual: np.ndarray, predicted: np.ndarray): """ Symmetric Mean Absolute Percentage Error Note: result is NOT multiplied by 100 """ return np.mean( 2.0 * np.abs(actual - predicted) / ((np.abs(actual) + np.abs(predicted)) + EPSILON) )
[docs]def smdape(actual: np.ndarray, predicted: np.ndarray): """ Symmetric Median Absolute Percentage Error Note: result is NOT multiplied by 100 """ return np.median( 2.0 * np.abs(actual - predicted) / ((np.abs(actual) + np.abs(predicted)) + EPSILON) )
[docs]def maape(actual: np.ndarray, predicted: np.ndarray): """ Mean Arctangent Absolute Percentage Error Note: result is NOT multiplied by 100 """ return np.mean(np.arctan(np.abs((actual - predicted) / (actual + EPSILON))))
[docs]def mase(actual: np.ndarray, predicted: np.ndarray, seasonality: int = 1): """ Mean Absolute Scaled Error Baseline (benchmark) is computed with naive forecasting (shifted by @seasonality) """ return mae(actual, predicted) / mae( actual[seasonality:], _naive_forecasting(actual, seasonality) )
[docs]def std_ae(actual: np.ndarray, predicted: np.ndarray): """Normalized Absolute Error""" __mae = mae(actual, predicted) return np.sqrt( np.sum(np.square(_error(actual, predicted) - __mae)) / (len(actual) - 1) )
[docs]def std_ape(actual: np.ndarray, predicted: np.ndarray): """Normalized Absolute Percentage Error""" __mape = mape(actual, predicted) return np.sqrt( np.sum(np.square(_percentage_error(actual, predicted) - __mape)) / (len(actual) - 1) )
[docs]def rmspe(actual: np.ndarray, predicted: np.ndarray): """ Root Mean Squared Percentage Error Note: result is NOT multiplied by 100 """ return np.sqrt(np.mean(np.square(_percentage_error(actual, predicted))))
[docs]def rmdspe(actual: np.ndarray, predicted: np.ndarray): """ Root Median Squared Percentage Error Note: result is NOT multiplied by 100 """ return np.sqrt(np.median(np.square(_percentage_error(actual, predicted))))
[docs]def rmsse(actual: np.ndarray, predicted: np.ndarray, seasonality: int = 1): """Root Mean Squared Scaled Error""" q = np.abs(_error(actual, predicted)) / mae( actual[seasonality:], _naive_forecasting(actual, seasonality) ) return np.sqrt(np.mean(np.square(q)))
[docs]def inrse(actual: np.ndarray, predicted: np.ndarray): """Integral Normalized Root Squared Error""" return np.sqrt( np.sum(np.square(_error(actual, predicted))) / np.sum(np.square(actual - np.mean(actual))) )
[docs]def rrse(actual: np.ndarray, predicted: np.ndarray): """Root Relative Squared Error""" return np.sqrt( np.sum(np.square(actual - predicted)) / np.sum(np.square(actual - np.mean(actual))) )
[docs]def mre(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Mean Relative Error""" return np.mean(_relative_error(actual, predicted, benchmark))
[docs]def rae(actual: np.ndarray, predicted: np.ndarray): """Relative Absolute Error (aka Approximation Error)""" return np.sum(np.abs(actual - predicted)) / ( np.sum(np.abs(actual - np.mean(actual))) + EPSILON )
[docs]def mrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Mean Relative Absolute Error""" return np.mean(np.abs(_relative_error(actual, predicted, benchmark)))
[docs]def mdrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Median Relative Absolute Error""" return np.median(np.abs(_relative_error(actual, predicted, benchmark)))
[docs]def gmrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Geometric Mean Relative Absolute Error""" return _geometric_mean(np.abs(_relative_error(actual, predicted, benchmark)))
[docs]def mbrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Mean Bounded Relative Absolute Error""" return np.mean(_bounded_relative_error(actual, predicted, benchmark))
[docs]def umbrae(actual: np.ndarray, predicted: np.ndarray, benchmark: np.ndarray = None): """Unscaled Mean Bounded Relative Absolute Error""" __mbrae = mbrae(actual, predicted, benchmark) return __mbrae / (1 - __mbrae)
[docs]def mda(actual: np.ndarray, predicted: np.ndarray): """Mean Directional Accuracy""" return np.mean( ( np.sign(actual[1:] - actual[:-1]) == np.sign(predicted[1:] - predicted[:-1]) ).astype(int) )