Repository: sktime/sktime

Issue:
[ENH] Loosen the restrictions on the numpy version requirement
Numpy 1.23 has been out for almost half a year now, and numpy 1.24 is on the horizon, as it is currentlly in the RC2 stage.
But sktime 0.14.1 has the requirement `numpy>=1.21.0,<1.23`.

See also:
* https://numpy.org/doc/stable/release.html
* https://github.com/numpy/numpy/releases/tag/v1.23.0
* https://github.com/numpy/numpy/releases/tag/v1.24.0rc2



Discussion:
@jorenham I can do this if you'd like. So change it to `<1.24` in the `pyproject.toml`?
@phershbe That would be great :). 

And if you don't see any potential issues with the upcoming 1.24 release, you could even set it `<1.25`, since no new breaking changes will be introduced in 1.24 in its RC2 stage. Especially since 1.24 comes with significant performance improvements.
Hello, 

I am facing the same issue on multiple projects for which I just bumped `numpy` to the latest version.
My two cents: I agree with @jorenham, 1.24 is on the way and this will also be important for Python 3.11 support, so bumping to 1.25 seems a better approach to me.

Resolving patch:
diff --git a/sktime/performance_metrics/forecasting/_classes.py b/sktime/performance_metrics/forecasting/_classes.py
--- a/sktime/performance_metrics/forecasting/_classes.py
+++ b/sktime/performance_metrics/forecasting/_classes.py
@@ -1367,30 +1367,30 @@ class GeometricMeanSquaredError(BaseForecastingErrorMetricFunc):
     >>> y_true = np.array([3, -0.5, 2, 7, 2])
     >>> y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
     >>> gmse = GeometricMeanSquaredError()
-    >>> gmse(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     2.80399089461488e-07
     >>> rgmse = GeometricMeanSquaredError(square_root=True)
-    >>> rgmse(y_true, y_pred)
+    >>> rgmse(y_true, y_pred)  # doctest: +SKIP
     0.000529527232030127
     >>> y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
     >>> y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
     >>> gmse = GeometricMeanSquaredError()
-    >>> gmse(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     0.5000000000115499
     >>> rgmse = GeometricMeanSquaredError(square_root=True)
-    >>> rgmse(y_true, y_pred)
+    >>> rgmse(y_true, y_pred)  # doctest: +SKIP
     0.5000024031086919
     >>> gmse = GeometricMeanSquaredError(multioutput='raw_values')
-    >>> gmse(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     array([2.30997255e-11, 1.00000000e+00])
     >>> rgmse = GeometricMeanSquaredError(multioutput='raw_values', square_root=True)
-    >>> rgmse(y_true, y_pred)
+    >>> rgmse(y_true, y_pred)# doctest: +SKIP
     array([4.80621738e-06, 1.00000000e+00])
     >>> gmse = GeometricMeanSquaredError(multioutput=[0.3, 0.7])
-    >>> gmse(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     0.7000000000069299
     >>> rgmse = GeometricMeanSquaredError(multioutput=[0.3, 0.7], square_root=True)
-    >>> rgmse(y_true, y_pred)
+    >>> rgmse(y_true, y_pred)  # doctest: +SKIP
     0.7000014418652152
     """
 
@@ -2101,26 +2101,26 @@ class MeanAsymmetricError(BaseForecastingErrorMetricFunc):
     >>> y_true = np.array([3, -0.5, 2, 7, 2])
     >>> y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
     >>> asymmetric_error = MeanAsymmetricError()
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     0.5
     >>> asymmetric_error = MeanAsymmetricError(left_error_function='absolute', \
     right_error_function='squared')
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     0.4625
     >>> y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
     >>> y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
     >>> asymmetric_error = MeanAsymmetricError()
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     0.75
     >>> asymmetric_error = MeanAsymmetricError(left_error_function='absolute', \
     right_error_function='squared')
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     0.7083333333333334
     >>> asymmetric_error = MeanAsymmetricError(multioutput='raw_values')
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     array([0.5, 1. ])
     >>> asymmetric_error = MeanAsymmetricError(multioutput=[0.3, 0.7])
-    >>> asymmetric_error(y_true, y_pred)
+    >>> asymmetric_error(y_true, y_pred)  # doctest: +SKIP
     0.85
     """
 
@@ -2204,27 +2204,27 @@ class MeanLinexError(BaseForecastingErrorMetricFunc):
     >>> linex_error = MeanLinexError()
     >>> y_true = np.array([3, -0.5, 2, 7, 2])
     >>> y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.19802627763937575
     >>> linex_error = MeanLinexError(b=2)
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.3960525552787515
     >>> linex_error = MeanLinexError(a=-1)
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.2391800623225643
     >>> y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
     >>> y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
     >>> linex_error = MeanLinexError()
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.2700398392309829
     >>> linex_error = MeanLinexError(a=-1)
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.49660966225813563
     >>> linex_error = MeanLinexError(multioutput='raw_values')
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     array([0.17220024, 0.36787944])
     >>> linex_error = MeanLinexError(multioutput=[0.3, 0.7])
-    >>> linex_error(y_true, y_pred)
+    >>> linex_error(y_true, y_pred)  # doctest: +SKIP
     0.30917568000716666
     """
 
diff --git a/sktime/performance_metrics/forecasting/_functions.py b/sktime/performance_metrics/forecasting/_functions.py
--- a/sktime/performance_metrics/forecasting/_functions.py
+++ b/sktime/performance_metrics/forecasting/_functions.py
@@ -142,21 +142,21 @@ def mean_linex_error(
     >>> from sktime.performance_metrics.forecasting import mean_linex_error
     >>> y_true = np.array([3, -0.5, 2, 7, 2])
     >>> y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
-    >>> mean_linex_error(y_true, y_pred)
+    >>> mean_linex_error(y_true, y_pred)  # doctest: +SKIP
     0.19802627763937575
-    >>> mean_linex_error(y_true, y_pred, b=2)
+    >>> mean_linex_error(y_true, y_pred, b=2)  # doctest: +SKIP
     0.3960525552787515
-    >>> mean_linex_error(y_true, y_pred, a=-1)
+    >>> mean_linex_error(y_true, y_pred, a=-1)  # doctest: +SKIP
     0.2391800623225643
     >>> y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
     >>> y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
-    >>> mean_linex_error(y_true, y_pred)
+    >>> mean_linex_error(y_true, y_pred)  # doctest: +SKIP
     0.2700398392309829
-    >>> mean_linex_error(y_true, y_pred, a=-1)
+    >>> mean_linex_error(y_true, y_pred, a=-1)  # doctest: +SKIP
     0.49660966225813563
-    >>> mean_linex_error(y_true, y_pred, multioutput='raw_values')
+    >>> mean_linex_error(y_true, y_pred, multioutput='raw_values')  # doctest: +SKIP
     array([0.17220024, 0.36787944])
-    >>> mean_linex_error(y_true, y_pred, multioutput=[0.3, 0.7])
+    >>> mean_linex_error(y_true, y_pred, multioutput=[0.3, 0.7])  # doctest: +SKIP
     0.30917568000716666
     """
     _, y_true, y_pred, multioutput = _check_reg_targets(y_true, y_pred, multioutput)
@@ -1396,28 +1396,28 @@ def geometric_mean_squared_error(
     --------
     >>> import numpy as np
     >>> from sktime.performance_metrics.forecasting import \
-    geometric_mean_squared_error
+    geometric_mean_squared_error as gmse
     >>> y_true = np.array([3, -0.5, 2, 7, 2])
     >>> y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
-    >>> geometric_mean_squared_error(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     2.80399089461488e-07
-    >>> geometric_mean_squared_error(y_true, y_pred, square_root=True)
+    >>> gmse(y_true, y_pred, square_root=True)  # doctest: +SKIP
     0.000529527232030127
     >>> y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
     >>> y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
-    >>> geometric_mean_squared_error(y_true, y_pred)
+    >>> gmse(y_true, y_pred)  # doctest: +SKIP
     0.5000000000115499
-    >>> geometric_mean_squared_error(y_true, y_pred, square_root=True)
+    >>> gmse(y_true, y_pred, square_root=True)  # doctest: +SKIP
     0.5000024031086919
-    >>> geometric_mean_squared_error(y_true, y_pred, multioutput='raw_values')
+    >>> gmse(y_true, y_pred, multioutput='raw_values')  # doctest: +SKIP
     array([2.30997255e-11, 1.00000000e+00])
-    >>> geometric_mean_squared_error(y_true, y_pred, multioutput='raw_values', \
-    square_root=True)
+    >>> gmse(y_true, y_pred, multioutput='raw_values', \
+    square_root=True)  # doctest: +SKIP
     array([4.80621738e-06, 1.00000000e+00])
-    >>> geometric_mean_squared_error(y_true, y_pred, multioutput=[0.3, 0.7])
+    >>> gmse(y_true, y_pred, multioutput=[0.3, 0.7])  # doctest: +SKIP
     0.7000000000069299
-    >>> geometric_mean_squared_error(y_true, y_pred, multioutput=[0.3, 0.7], \
-    square_root=True)
+    >>> gmse(y_true, y_pred, multioutput=[0.3, 0.7], \
+    square_root=True)  # doctest: +SKIP
     0.7000014418652152
     """
     _, y_true, y_pred, multioutput = _check_reg_targets(y_true, y_pred, multioutput)


Test patch:
diff --git a/sktime/performance_metrics/forecasting/tests/__init__.py b/sktime/performance_metrics/forecasting/tests/__init__.py
new file mode 100644
--- /dev/null
+++ b/sktime/performance_metrics/forecasting/tests/__init__.py
@@ -0,0 +1,2 @@
+# -*- coding: utf-8 -*-
+"""Tests for sktime performance metrics module."""
diff --git a/sktime/performance_metrics/forecasting/tests/test_metrics.py b/sktime/performance_metrics/forecasting/tests/test_metrics.py
new file mode 100644
--- /dev/null
+++ b/sktime/performance_metrics/forecasting/tests/test_metrics.py
@@ -0,0 +1,109 @@
+# -*- coding: utf-8 -*-
+# copyright: sktime developers, BSD-3-Clause License (see LICENSE file)
+"""Tests for some metrics."""
+# currently this consists entirely of doctests from _classes and _functions
+# since the numpy output print changes between versions
+
+import numpy as np
+
+
+def test_gmse_class():
+    """Doctest from GeometricMeanSquaredError."""
+    from sktime.performance_metrics.forecasting import GeometricMeanSquaredError
+
+    y_true = np.array([3, -0.5, 2, 7, 2])
+    y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
+    gmse = GeometricMeanSquaredError()
+
+    assert np.allclose(gmse(y_true, y_pred), 2.80399089461488e-07)
+    rgmse = GeometricMeanSquaredError(square_root=True)
+    assert np.allclose(rgmse(y_true, y_pred), 0.000529527232030127)
+
+    y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
+    y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
+    gmse = GeometricMeanSquaredError()
+    assert np.allclose(gmse(y_true, y_pred), 0.5000000000115499)
+    rgmse = GeometricMeanSquaredError(square_root=True)
+    assert np.allclose(rgmse(y_true, y_pred), 0.5000024031086919)
+    gmse = GeometricMeanSquaredError(multioutput="raw_values")
+    assert np.allclose(gmse(y_true, y_pred), np.array([2.30997255e-11, 1.00000000e00]))
+    rgmse = GeometricMeanSquaredError(multioutput="raw_values", square_root=True)
+    assert np.allclose(rgmse(y_true, y_pred), np.array([4.80621738e-06, 1.00000000e00]))
+    gmse = GeometricMeanSquaredError(multioutput=[0.3, 0.7])
+    assert np.allclose(gmse(y_true, y_pred), 0.7000000000069299)
+    rgmse = GeometricMeanSquaredError(multioutput=[0.3, 0.7], square_root=True)
+    assert np.allclose(rgmse(y_true, y_pred), 0.7000014418652152)
+
+
+def test_gmse_function():
+    """Doctest from geometric_mean_squared_error."""
+    from sktime.performance_metrics.forecasting import geometric_mean_squared_error
+
+    gmse = geometric_mean_squared_error
+    y_true = np.array([3, -0.5, 2, 7, 2])
+    y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
+    assert np.allclose(gmse(y_true, y_pred), 2.80399089461488e-07)
+    assert np.allclose(gmse(y_true, y_pred, square_root=True), 0.000529527232030127)
+    y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
+    y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
+    assert np.allclose(gmse(y_true, y_pred), 0.5000000000115499)
+    assert np.allclose(gmse(y_true, y_pred, square_root=True), 0.5000024031086919)
+    assert np.allclose(
+        gmse(y_true, y_pred, multioutput="raw_values"),
+        np.array([2.30997255e-11, 1.00000000e00]),
+    )
+    assert np.allclose(
+        gmse(y_true, y_pred, multioutput="raw_values", square_root=True),
+        np.array([4.80621738e-06, 1.00000000e00]),
+    )
+    assert np.allclose(gmse(y_true, y_pred, multioutput=[0.3, 0.7]), 0.7000000000069299)
+    assert np.allclose(
+        gmse(y_true, y_pred, multioutput=[0.3, 0.7], square_root=True),
+        0.7000014418652152,
+    )
+
+
+def test_linex_class():
+    """Doctest from MeanLinexError."""
+    from sktime.performance_metrics.forecasting import MeanLinexError
+
+    linex_error = MeanLinexError()
+    y_true = np.array([3, -0.5, 2, 7, 2])
+    y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
+    assert np.allclose(linex_error(y_true, y_pred), 0.19802627763937575)
+    linex_error = MeanLinexError(b=2)
+    assert np.allclose(linex_error(y_true, y_pred), 0.3960525552787515)
+    linex_error = MeanLinexError(a=-1)
+    assert np.allclose(linex_error(y_true, y_pred), 0.2391800623225643)
+    y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
+    y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
+    linex_error = MeanLinexError()
+    assert np.allclose(linex_error(y_true, y_pred), 0.2700398392309829)
+    linex_error = MeanLinexError(a=-1)
+    assert np.allclose(linex_error(y_true, y_pred), 0.49660966225813563)
+    linex_error = MeanLinexError(multioutput="raw_values")
+    assert np.allclose(linex_error(y_true, y_pred), np.array([0.17220024, 0.36787944]))
+    linex_error = MeanLinexError(multioutput=[0.3, 0.7])
+    assert np.allclose(linex_error(y_true, y_pred), 0.30917568000716666)
+
+
+def test_linex_function():
+    """Doctest from mean_linex_error."""
+    from sktime.performance_metrics.forecasting import mean_linex_error
+
+    y_true = np.array([3, -0.5, 2, 7, 2])
+    y_pred = np.array([2.5, 0.0, 2, 8, 1.25])
+    assert np.allclose(mean_linex_error(y_true, y_pred), 0.19802627763937575)
+    assert np.allclose(mean_linex_error(y_true, y_pred, b=2), 0.3960525552787515)
+    assert np.allclose(mean_linex_error(y_true, y_pred, a=-1), 0.2391800623225643)
+    y_true = np.array([[0.5, 1], [-1, 1], [7, -6]])
+    y_pred = np.array([[0, 2], [-1, 2], [8, -5]])
+    assert np.allclose(mean_linex_error(y_true, y_pred), 0.2700398392309829)
+    assert np.allclose(mean_linex_error(y_true, y_pred, a=-1), 0.49660966225813563)
+    assert np.allclose(
+        mean_linex_error(y_true, y_pred, multioutput="raw_values"),
+        np.array([0.17220024, 0.36787944]),
+    )
+    assert np.allclose(
+        mean_linex_error(y_true, y_pred, multioutput=[0.3, 0.7]), 0.30917568000716666
+    )