From 59a79487be7e53487a2378231f5b1f98c735a9f4 Mon Sep 17 00:00:00 2001 From: SrinidhiPatil Date: Tue, 24 Mar 2020 10:15:27 +0530 Subject: [PATCH 1/3] Added GMAE Metric --- gtime/metrics/__init__.py | 4 +- gtime/metrics/metrics.py | 43 +++++++++++- gtime/metrics/tests/test_loss_functions.py | 76 ++++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) diff --git a/gtime/metrics/__init__.py b/gtime/metrics/__init__.py index 54a7c85..3dac9ff 100755 --- a/gtime/metrics/__init__.py +++ b/gtime/metrics/__init__.py @@ -2,7 +2,8 @@ The :mod:`gtime.metrics` module contains a collection of different metrics. """ -from .metrics import smape, max_error, mse, log_mse, r_square, mae, mape, rmse, rmsle +from .metrics import (smape, max_error, mse, log_mse, + r_square, mae, mape, rmse, rmsle, gmae) __all__ = [ "smape", @@ -14,4 +15,5 @@ "r_square", "mae", "mape", + "gmae", ] diff --git a/gtime/metrics/metrics.py b/gtime/metrics/metrics.py index 133cb91..a9b4a18 100644 --- a/gtime/metrics/metrics.py +++ b/gtime/metrics/metrics.py @@ -2,6 +2,7 @@ import numpy as np import pandas as pd +from scipy.stats import gmean def _check_input(y_true: np.ndarray, y_pred: np.ndarray) -> None: @@ -305,7 +306,8 @@ def mae( y_true: Union[pd.DataFrame, List, np.ndarray], y_pred: Union[pd.DataFrame, List, np.ndarray], ) -> float: - """Compute the Mean Absolute Error(also called, Mean Absolute Deviation(MAD)) between two vectors. + """Compute the Mean Absolute Error(also called, Mean Absolute Deviation(MAD) or Mean Ratio) + between two vectors. Parameters ---------- @@ -371,3 +373,42 @@ def mape( if np.isnan(mape_value): raise ValueError("MAPE can not be calculated due to Zero/Zero") return mape_value * 100 + + +def gmae( + y_true: Union[pd.DataFrame, List, np.ndarray], + y_pred: Union[pd.DataFrame, List, np.ndarray], +) -> float: + """Compute the Geometric Mean Absolute Error between two vectors. + + Parameters + ---------- + y_true : array-like, shape (length, 1), required + The first vector. + + y_pred : array-like, shape (length, 1), required + The second vector. + + Returns + ------- + gmae_value : float + The geometric mean absolute error between the two vectors. + + Examples + -------- + >>> from gtime.metrics import gmae + >>> y_true = [0, 1, 2, 3, 6, 5] + >>> y_pred = [-1, 4, 5, 10, 4, 1] + >>> gmae(y_true, y_pred) + 2.82 + + """ + y_true, y_pred = _convert_to_ndarray(y_true, y_pred) + _check_input(y_true, y_pred) + + absolute_difference = np.abs(y_pred - y_true) + if (0 in absolute_difference): + return 0 + else: + gmae_value = gmean(absolute_difference) + return gmae_value diff --git a/gtime/metrics/tests/test_loss_functions.py b/gtime/metrics/tests/test_loss_functions.py index 5a0ddd0..02b22cd 100644 --- a/gtime/metrics/tests/test_loss_functions.py +++ b/gtime/metrics/tests/test_loss_functions.py @@ -1,6 +1,7 @@ import numpy as np import pandas as pd import pytest +from scipy.stats import gmean from hypothesis import given from hypothesis.extra.numpy import arrays from hypothesis.strategies import floats @@ -15,6 +16,7 @@ mape, rmse, rmsle, + gmae, ) @@ -812,3 +814,77 @@ def test_mape_random_arrays_finite_values(self, y_true, y_pred): print(y_pred) assert expected_mape == mape_value + + +class TestGMAE: + def _correct_gmae(self, y_true, y_pred): + y_true = np.array(y_true) + y_pred = np.array(y_pred) + + absolute_difference = np.abs(y_true - y_pred) + if (0 in absolute_difference): + return 0 + else: + gmae = gmean(absolute_difference) + return gmae + + def test_wrong_vector_length(self): + y_true = np.random.random(5) + y_pred = np.random.random(4) + + with pytest.raises(ValueError): + gmae(y_true, y_pred) + + def test_nan_values(self): + y_true = [np.nan, 1, 2, 3] + y_pred = np.random.random(4) + + with pytest.raises(ValueError): + gmae(y_true, y_pred) + + def test_infinite_values(self): + y_true = np.random.random(4) + y_pred = [0, np.inf, 2, 3] + + with pytest.raises(ValueError): + gmae(y_true, y_pred) + + def test_gmae_list(self): + y_true = [0, 1, 2, 3, 6, 5] + y_pred = [-1, 4, 5, 10, 4, 1] + + gmae_value = np.round(gmae(y_true, y_pred), decimals=2) + expected_gmae = 2.82 + + assert expected_gmae == gmae_value + + def test_gmae_array(self): + y_true = np.array([0, 1, 2, 3, 6, 5]) + y_pred = np.array([-1, 4, 5, 10, 4, 1]) + + gmae_value = np.round(gmae(y_true, y_pred), decimals=2) + expected_gmae = 2.82 + + assert expected_gmae == gmae_value + + def test_gmae_dataframe(self): + y_true = pd.DataFrame([0, 1, 2, 3, 6, 5]) + y_pred = pd.DataFrame([-1, 4, 5, 10, 4, 1]) + + gmae_value = np.round(gmae(y_true, y_pred), decimals=2) + expected_gmae = 2.82 + + assert expected_gmae == gmae_value + + @given( + arrays(float, shape=30, elements=floats(allow_nan=False, allow_infinity=False)), + arrays(float, shape=30, elements=floats(allow_nan=False, allow_infinity=False)), + ) + def test_gmae_random_arrays_finite_values(self, y_true, y_pred): + gmae_value = gmae(y_true, y_pred) + expected_gmae = self._correct_gmae(y_true, y_pred) + print(y_true) + print(y_pred) + + assert expected_gmae == gmae_value + From dfa694c2dc295e837c06ed8fa94ec3a377c173dc Mon Sep 17 00:00:00 2001 From: SrinidhiPatil Date: Tue, 24 Mar 2020 10:24:36 +0530 Subject: [PATCH 2/3] Added GMAE metric --- gtime/metrics/tests/test_loss_functions.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/gtime/metrics/tests/test_loss_functions.py b/gtime/metrics/tests/test_loss_functions.py index 02b22cd..0ce755a 100644 --- a/gtime/metrics/tests/test_loss_functions.py +++ b/gtime/metrics/tests/test_loss_functions.py @@ -876,6 +876,16 @@ def test_gmae_dataframe(self): assert expected_gmae == gmae_value + def test_zero_in_difference_gmae(self): + #if absolute difference is zero then GMAE is zero + y_true = pd.DataFrame([0, 1, 2, 3, 4, 5]) + y_pred = pd.DataFrame([-1, 4, 5, 10, 4, 1]) + + gmae_value = np.round(gmae(y_true, y_pred), decimals=2) + expected_gmae = 0 + + assert expected_gmae == gmae_value + @given( arrays(float, shape=30, elements=floats(allow_nan=False, allow_infinity=False)), arrays(float, shape=30, elements=floats(allow_nan=False, allow_infinity=False)), From bb6964d7695a37ad817a851144e445b81bca61da Mon Sep 17 00:00:00 2001 From: SrinidhiPatil Date: Tue, 24 Mar 2020 23:38:31 +0530 Subject: [PATCH 3/3] Implemented Review Changes - GMAE --- gtime/metrics/metrics.py | 6 +----- gtime/metrics/tests/test_loss_functions.py | 6 +----- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/gtime/metrics/metrics.py b/gtime/metrics/metrics.py index a9b4a18..8f32950 100644 --- a/gtime/metrics/metrics.py +++ b/gtime/metrics/metrics.py @@ -407,8 +407,4 @@ def gmae( _check_input(y_true, y_pred) absolute_difference = np.abs(y_pred - y_true) - if (0 in absolute_difference): - return 0 - else: - gmae_value = gmean(absolute_difference) - return gmae_value + return 0 if 0 in absolute_difference else gmean(absolute_difference) diff --git a/gtime/metrics/tests/test_loss_functions.py b/gtime/metrics/tests/test_loss_functions.py index 0ce755a..47b872a 100644 --- a/gtime/metrics/tests/test_loss_functions.py +++ b/gtime/metrics/tests/test_loss_functions.py @@ -822,11 +822,7 @@ def _correct_gmae(self, y_true, y_pred): y_pred = np.array(y_pred) absolute_difference = np.abs(y_true - y_pred) - if (0 in absolute_difference): - return 0 - else: - gmae = gmean(absolute_difference) - return gmae + return 0 if 0 in absolute_difference else gmean(absolute_difference) def test_wrong_vector_length(self): y_true = np.random.random(5)