From cd7a02a4db7e760b881f3feeb832ffd84fa8645a Mon Sep 17 00:00:00 2001 From: abel Date: Thu, 2 Sep 2021 16:34:42 +0200 Subject: MAINT, ENH [#10736] Add interpolation methods to quantile - Added the missing linear interpolation methods. - Updated the existing unit tests. - Added pytest.mark.xfail for boolean arrays See - https://github.com/numpy/numpy/pull/19857#issuecomment-919258693 - https://github.com/numpy/numpy/issues/19154 --- numpy/lib/tests/test_function_base.py | 177 +++++++++++++++++++++++++++------- 1 file changed, 141 insertions(+), 36 deletions(-) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index c7dfe5673..4f0db7bdb 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -2903,36 +2903,134 @@ class TestPercentile: [1, 1, 1]]) assert_array_equal(np.percentile(x, 50, axis=0), [1, 1, 1]) - def test_linear(self): - + @pytest.mark.parametrize("dtype", np.typecodes["AllFloat"]) + def test_linear_nan_1D(self, dtype): + # METHOD 1 of H&F + arr = np.asarray([15.0, np.NAN, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="linear") + np.testing.assert_equal(res, np.NAN) + np.testing.assert_equal(res.dtype, arr.dtype) + + TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O" + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_inverted_cdf(self, dtype): + # METHOD 1 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="inverted_cdf") + np.testing.assert_almost_equal(res, 20, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_averaged_inverted_cdf(self, dtype): + # METHOD 2 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="averaged_inverted_cdf") + np.testing.assert_almost_equal(res, 27.5, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_closest_observation(self, dtype): + # METHOD 3 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="closest_observation") + np.testing.assert_almost_equal(res, 20, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_interpolated_inverted_cdf(self, dtype): + # METHOD 4 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="interpolated_inverted_cdf") + np.testing.assert_almost_equal(res, 20, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_hazen(self, dtype): + # METHOD 5 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="hazen") + np.testing.assert_almost_equal(res, 27.5, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_weibull(self, dtype): + # METHOD 6 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="weibull") + np.testing.assert_almost_equal(res, 26, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_linear(self, dtype): + # METHOD 7 of H&F # Test defaults assert_equal(np.percentile(range(10), 50), 4.5) - - # explicitly specify interpolation_method 'linear' (the default) - assert_equal(np.percentile(range(10), 50, - interpolation='linear'), 4.5) - - def test_lower_higher(self): - - # interpolation_method 'lower'/'higher' - assert_equal(np.percentile(range(10), 50, + # explicit interpolation_method (the default) + res = np.percentile([15.0, 20.0, 35.0, 40.0, 50.0], + 40, + interpolation="linear") + np.testing.assert_almost_equal(res, 29, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_median_unbiased(self, dtype): + # METHOD 8 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="median_unbiased") + np.testing.assert_almost_equal(res, 27, 14) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_linear_normal_unbiased(self, dtype): + # METHOD 9 of H&F + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) + res = np.percentile( + arr, + 40.0, + interpolation="normal_unbiased") + np.testing.assert_almost_equal(res, 27.125, 15) + + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_lower_higher(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 50, interpolation='lower'), 4) - assert_equal(np.percentile(range(10), 50, + assert_equal(np.percentile(np.arange(10, dtype=dtype), 50, interpolation='higher'), 5) - def test_midpoint(self): - assert_equal(np.percentile(range(10), 51, + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_midpoint(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 51, interpolation='midpoint'), 4.5) - assert_equal(np.percentile(range(11), 51, + assert_equal(np.percentile(np.arange(9, dtype=dtype) + 1, 50, + interpolation='midpoint'), 5) + assert_equal(np.percentile(np.arange(11, dtype=dtype), 51, interpolation='midpoint'), 5.5) - assert_equal(np.percentile(range(11), 50, + assert_equal(np.percentile(np.arange(11, dtype=dtype), 50, interpolation='midpoint'), 5) - def test_nearest(self): - assert_equal(np.percentile(range(10), 51, + @pytest.mark.parametrize("dtype", TYPE_CODES) + def test_nearest(self, dtype): + assert_equal(np.percentile(np.arange(10, dtype=dtype), 51, interpolation='nearest'), 5) - assert_equal(np.percentile(range(10), 49, - interpolation='nearest'), 4) + assert_equal(np.percentile(np.arange(10, dtype=dtype), 49, + interpolation='nearest'), 4) def test_sequence(self): x = np.arange(8) * 0.5 @@ -3038,18 +3136,18 @@ class TestPercentile: y = np.zeros((3,)) p = (1, 2, 3) np.percentile(x, p, out=y) - assert_equal(y, np.percentile(x, p)) + assert_equal(np.percentile(x, p), y) x = np.array([[1, 2, 3], [4, 5, 6]]) y = np.zeros((3, 3)) np.percentile(x, p, axis=0, out=y) - assert_equal(y, np.percentile(x, p, axis=0)) + assert_equal(np.percentile(x, p, axis=0), y) y = np.zeros((3, 2)) np.percentile(x, p, axis=1, out=y) - assert_equal(y, np.percentile(x, p, axis=1)) + assert_equal(np.percentile(x, p, axis=1), y) x = np.arange(12).reshape(3, 4) # q.dim > 1, float @@ -3293,6 +3391,7 @@ class TestPercentile: with pytest.raises(ValueError, match="Percentiles must be in"): np.percentile([1, 2, 3, 4.0], q) + class TestQuantile: # most of this is already tested by TestPercentile @@ -3302,6 +3401,7 @@ class TestQuantile: assert_equal(np.quantile(x, 1), 3.5) assert_equal(np.quantile(x, 0.5), 1.75) + @pytest.mark.xfail(reason="See gh-19154") def test_correct_quantile_value(self): a = np.array([True]) tf_quant = np.quantile(True, False) @@ -3310,12 +3410,11 @@ class TestQuantile: a = np.array([False, True, True]) quant_res = np.quantile(a, a) assert_array_equal(quant_res, a) - assert_equal(a.dtype, quant_res.dtype) + assert_equal(quant_res.dtype, a.dtype) def test_fraction(self): # fractional input, integral quantile x = [Fraction(i, 2) for i in range(8)] - q = np.quantile(x, 0) assert_equal(q, 0) assert_equal(type(q), Fraction) @@ -3352,6 +3451,12 @@ class TestQuantile: np.quantile(np.arange(100.), p, interpolation="midpoint") assert_array_equal(p, p0) + @pytest.mark.parametrize("dtype", np.typecodes["AllInteger"]) + def test_quantile_preserve_int_type(self, dtype): + res = np.quantile(np.array([1, 2], dtype=dtype), [0.5], + interpolation="nearest") + assert res.dtype == dtype + def test_quantile_monotonic(self): # GH 14685 # test that the return value of quantile is monotonic if p0 is ordered @@ -3380,9 +3485,9 @@ class TestLerp: min_value=-1e300, max_value=1e300), b = st.floats(allow_nan=False, allow_infinity=False, min_value=-1e300, max_value=1e300)) - def test_lerp_monotonic(self, t0, t1, a, b): - l0 = np.lib.function_base._lerp(a, b, t0) - l1 = np.lib.function_base._lerp(a, b, t1) + def test_linear_interpolation_formula_monotonic(self, t0, t1, a, b): + l0 = nfb._linear_interpolation_formula(a, b, t0) + l1 = nfb._linear_interpolation_formula(a, b, t1) if t0 == t1 or a == b: assert l0 == l1 # uninteresting elif (t0 < t1) == (a < b): @@ -3396,11 +3501,11 @@ class TestLerp: min_value=-1e300, max_value=1e300), b=st.floats(allow_nan=False, allow_infinity=False, min_value=-1e300, max_value=1e300)) - def test_lerp_bounded(self, t, a, b): + def test_linear_interpolation_formula_bounded(self, t, a, b): if a <= b: - assert a <= np.lib.function_base._lerp(a, b, t) <= b + assert a <= nfb._linear_interpolation_formula(a, b, t) <= b else: - assert b <= np.lib.function_base._lerp(a, b, t) <= a + assert b <= nfb._linear_interpolation_formula(a, b, t) <= a @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=1), @@ -3408,17 +3513,17 @@ class TestLerp: min_value=-1e300, max_value=1e300), b=st.floats(allow_nan=False, allow_infinity=False, min_value=-1e300, max_value=1e300)) - def test_lerp_symmetric(self, t, a, b): + def test_linear_interpolation_formula_symmetric(self, t, a, b): # double subtraction is needed to remove the extra precision of t < 0.5 - left = np.lib.function_base._lerp(a, b, 1 - (1 - t)) - right = np.lib.function_base._lerp(b, a, 1 - t) + left = nfb._linear_interpolation_formula(a, b, 1 - (1 - t)) + right = nfb._linear_interpolation_formula(b, a, 1 - t) assert left == right - def test_lerp_0d_inputs(self): + def test_linear_interpolation_formula_0d_inputs(self): a = np.array(2) b = np.array(5) t = np.array(0.2) - assert np.lib.function_base._lerp(a, b, t) == 2.6 + assert nfb._linear_interpolation_formula(a, b, t) == 2.6 class TestMedian: -- cgit v1.2.1 From ab19ed256bf9b20340c92cebcfd6158241122c88 Mon Sep 17 00:00:00 2001 From: abel Date: Tue, 19 Oct 2021 12:10:16 +0200 Subject: Fix _lerp - some changes were unrelated to the PR and have been reverted, including, renaming and moving the logic around. - Also renamed _quantile_ureduce_func to its original name --- numpy/lib/tests/test_function_base.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 4f0db7bdb..d59f3a85d 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -3486,8 +3486,8 @@ class TestLerp: b = st.floats(allow_nan=False, allow_infinity=False, min_value=-1e300, max_value=1e300)) def test_linear_interpolation_formula_monotonic(self, t0, t1, a, b): - l0 = nfb._linear_interpolation_formula(a, b, t0) - l1 = nfb._linear_interpolation_formula(a, b, t1) + l0 = nfb._lerp(a, b, t0) + l1 = nfb._lerp(a, b, t1) if t0 == t1 or a == b: assert l0 == l1 # uninteresting elif (t0 < t1) == (a < b): @@ -3503,9 +3503,9 @@ class TestLerp: min_value=-1e300, max_value=1e300)) def test_linear_interpolation_formula_bounded(self, t, a, b): if a <= b: - assert a <= nfb._linear_interpolation_formula(a, b, t) <= b + assert a <= nfb._lerp(a, b, t) <= b else: - assert b <= nfb._linear_interpolation_formula(a, b, t) <= a + assert b <= nfb._lerp(a, b, t) <= a @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False, min_value=0, max_value=1), @@ -3515,15 +3515,15 @@ class TestLerp: min_value=-1e300, max_value=1e300)) def test_linear_interpolation_formula_symmetric(self, t, a, b): # double subtraction is needed to remove the extra precision of t < 0.5 - left = nfb._linear_interpolation_formula(a, b, 1 - (1 - t)) - right = nfb._linear_interpolation_formula(b, a, 1 - t) + left = nfb._lerp(a, b, 1 - (1 - t)) + right = nfb._lerp(b, a, 1 - t) assert left == right def test_linear_interpolation_formula_0d_inputs(self): a = np.array(2) b = np.array(5) t = np.array(0.2) - assert nfb._linear_interpolation_formula(a, b, t) == 2.6 + assert nfb._lerp(a, b, t) == 2.6 class TestMedian: -- cgit v1.2.1 From 2a5422da7cb6759d75477738cc192fee3ca2a19c Mon Sep 17 00:00:00 2001 From: abel Date: Tue, 19 Oct 2021 17:55:06 +0200 Subject: Fix issue with nan scalar Also added unit test for it. --- numpy/lib/tests/test_function_base.py | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index d59f3a85d..f9854c568 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -3475,6 +3475,12 @@ class TestQuantile: quantile = np.quantile(arr, p0) assert_equal(np.sort(quantile), quantile) + def test_quantile_scalar_nan(self): + a = np.array([[10., 7., 4.], [3., 2., 1.]]) + a[0][1] = np.nan + actual = np.quantile(a, 0.5) + assert np.isscalar(actual) + assert_equal(np.quantile(a, 0.5), np.nan) class TestLerp: @hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False, -- cgit v1.2.1 From 98cf811e27138c41e365222cb70f06d70c0db4ee Mon Sep 17 00:00:00 2001 From: abel Date: Thu, 21 Oct 2021 12:31:06 +0200 Subject: TST: Add extrapolation tests --- numpy/lib/tests/test_function_base.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index f9854c568..53254c3e5 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -3032,6 +3032,15 @@ class TestPercentile: assert_equal(np.percentile(np.arange(10, dtype=dtype), 49, interpolation='nearest'), 4) + def test_linear_interpolation_extrapolation(self): + arr = np.random.rand(5) + + actual = np.percentile(arr, 100) + np.testing.assert_equal(actual, arr.max()) + + actual = np.percentile(arr, 0) + np.testing.assert_equal(actual, arr.min()) + def test_sequence(self): x = np.arange(8) * 0.5 assert_equal(np.percentile(x, [0, 100, 50]), [0, 3.5, 1.75]) -- cgit v1.2.1 From 2faf8edd635c8c700a3f5215cc747e268541b6bc Mon Sep 17 00:00:00 2001 From: abel Date: Thu, 21 Oct 2021 16:13:29 +0200 Subject: TST: Add parametrize for interpolation methods --- numpy/lib/tests/test_function_base.py | 134 +++++++++++----------------------- 1 file changed, 43 insertions(+), 91 deletions(-) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 53254c3e5..22228405a 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -2914,98 +2914,50 @@ class TestPercentile: np.testing.assert_equal(res, np.NAN) np.testing.assert_equal(res.dtype, arr.dtype) - TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O" - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_inverted_cdf(self, dtype): - # METHOD 1 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="inverted_cdf") - np.testing.assert_almost_equal(res, 20, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_averaged_inverted_cdf(self, dtype): - # METHOD 2 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="averaged_inverted_cdf") - np.testing.assert_almost_equal(res, 27.5, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_closest_observation(self, dtype): - # METHOD 3 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="closest_observation") - np.testing.assert_almost_equal(res, 20, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_interpolated_inverted_cdf(self, dtype): - # METHOD 4 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="interpolated_inverted_cdf") - np.testing.assert_almost_equal(res, 20, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_hazen(self, dtype): - # METHOD 5 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="hazen") - np.testing.assert_almost_equal(res, 27.5, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_weibull(self, dtype): - # METHOD 6 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="weibull") - np.testing.assert_almost_equal(res, 26, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_linear(self, dtype): - # METHOD 7 of H&F - # Test defaults - assert_equal(np.percentile(range(10), 50), 4.5) - # explicit interpolation_method (the default) - res = np.percentile([15.0, 20.0, 35.0, 40.0, 50.0], - 40, - interpolation="linear") - np.testing.assert_almost_equal(res, 29, 15) - - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_median_unbiased(self, dtype): - # METHOD 8 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="median_unbiased") - np.testing.assert_almost_equal(res, 27, 14) + H_F_TYPE_CODES = [(int_type, np.float64) + for int_type in np.typecodes["AllInteger"] + ] + [(np.float16, np.float64), + (np.float32, np.float64), + (np.float64, np.float64), + (np.float128, np.float128), + (np.complex64, np.complex128), + (np.complex128, np.complex128), + (np.complex256, np.complex256), + (np.dtype("O"), np.float64)] + + @pytest.mark.parametrize(["input_dtype", "expected_dtype"], H_F_TYPE_CODES) + @pytest.mark.parametrize(["interpolation", "expected"], + [("inverted_cdf", 20), + ("averaged_inverted_cdf", 27.5), + ("closest_observation", 20), + ("interpolated_inverted_cdf", 20), + ("hazen", 27.5), + ("weibull", 26), + ("linear", 29), + ("median_unbiased", 27), + ("normal_unbiased", 27.125), + ]) + def test_linear_interpolation(self, + interpolation, + expected, + input_dtype, + expected_dtype): + arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=input_dtype) + actual = np.percentile(arr, 40.0, interpolation=interpolation) + + np.testing.assert_almost_equal(actual, expected, 14) + + if interpolation in ["inverted_cdf", "closest_observation"]: + if input_dtype == "O": + np.testing.assert_equal(np.asarray(actual).dtype, np.float64) + else: + np.testing.assert_equal(np.asarray(actual).dtype, + np.dtype(input_dtype)) + else: + np.testing.assert_equal(np.asarray(actual).dtype, + np.dtype(expected_dtype)) - @pytest.mark.parametrize("dtype", TYPE_CODES) - def test_linear_normal_unbiased(self, dtype): - # METHOD 9 of H&F - arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=dtype) - res = np.percentile( - arr, - 40.0, - interpolation="normal_unbiased") - np.testing.assert_almost_equal(res, 27.125, 15) + TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["AllFloat"] + "O" @pytest.mark.parametrize("dtype", TYPE_CODES) def test_lower_higher(self, dtype): -- cgit v1.2.1 From a8218af306e7639f2136c8e915a5b9ea1f34356a Mon Sep 17 00:00:00 2001 From: abel Date: Fri, 22 Oct 2021 13:35:40 +0200 Subject: TST: Make use of clongdouble and longdouble On some platforms float128 and complex256 do not exist. Using (c)longdouble aliases should work on all platforms. --- numpy/lib/tests/test_function_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index 22228405a..d5fa012f1 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -2919,10 +2919,10 @@ class TestPercentile: ] + [(np.float16, np.float64), (np.float32, np.float64), (np.float64, np.float64), - (np.float128, np.float128), + (np.longdouble, np.longdouble), (np.complex64, np.complex128), (np.complex128, np.complex128), - (np.complex256, np.complex256), + (np.clongdouble, np.clongdouble), (np.dtype("O"), np.float64)] @pytest.mark.parametrize(["input_dtype", "expected_dtype"], H_F_TYPE_CODES) -- cgit v1.2.1 From 53e3df3c99a26791cc07e2ea1570e87643fdf7e0 Mon Sep 17 00:00:00 2001 From: abel Date: Tue, 9 Nov 2021 17:35:03 +0100 Subject: TST: Add test for max ulp in default quantile calculation --- numpy/lib/tests/test_function_base.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'numpy/lib/tests/test_function_base.py') diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py index d5fa012f1..1c274afae 100644 --- a/numpy/lib/tests/test_function_base.py +++ b/numpy/lib/tests/test_function_base.py @@ -3356,6 +3356,14 @@ class TestPercentile: class TestQuantile: # most of this is already tested by TestPercentile + def test_max_ulp(self): + x = [0.0, 0.2, 0.4] + a = np.quantile(x, 0.45) + # The default linear method would result in 0 + 0.2 * (0.45/2) = 0.18. + # 0.18 is not exactly representable and the formula leads to a 1 ULP + # different result. Ensure it is this exact within 1 ULP, see gh-20331. + np.testing.assert_array_max_ulp(a, 0.18, maxulp=1) + def test_basic(self): x = np.arange(8) * 0.5 assert_equal(np.quantile(x, 0), 0.) -- cgit v1.2.1