summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSebastian Berg <sebastian@sipsolutions.net>2020-06-27 11:35:27 -0500
committerGitHub <noreply@github.com>2020-06-27 11:35:27 -0500
commit4d5b2558c275a4a46b6ab0c486536d8e779651ce (patch)
tree29da4bc4873a88541fd3f59edec03f51c2930261
parentf156d37e63cf1f99635153e0022695285fc4a58d (diff)
parent88acbc852eeb73edd0d09c773f125725c8a0587b (diff)
downloadnumpy-4d5b2558c275a4a46b6ab0c486536d8e779651ce.tar.gz
Merge pull request #16273 from CloseChoice/BUG-order_percentile-monotonically
BUG: Order percentile monotonically
-rw-r--r--numpy/lib/function_base.py8
-rw-r--r--numpy/lib/tests/test_function_base.py71
2 files changed, 78 insertions, 1 deletions
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index cf6f4891c..1053598b1 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -3879,7 +3879,13 @@ def _quantile_is_valid(q):
def _lerp(a, b, t, out=None):
""" Linearly interpolate from a to b by a factor of t """
- return add(a*(1 - t), b*t, out=out)
+ diff_b_a = subtract(b, a)
+ # asanyarray is a stop-gap until gh-13105
+ lerp_interpolation = asanyarray(add(a, diff_b_a*t, out=out))
+ subtract(b, diff_b_a * (1 - t), out=lerp_interpolation, where=t>=0.5)
+ if lerp_interpolation.ndim == 0 and out is None:
+ lerp_interpolation = lerp_interpolation[()] # unpack 0d arrays
+ return lerp_interpolation
def _quantile_ureduce_func(a, q, axis=None, out=None, overwrite_input=False,
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index 9ba0be56a..5ccac62e4 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -5,6 +5,10 @@ import decimal
from fractions import Fraction
import math
import pytest
+import hypothesis
+from hypothesis.extra.numpy import arrays
+import hypothesis.strategies as st
+
import numpy as np
from numpy import ma
@@ -3104,6 +3108,73 @@ class TestQuantile:
np.quantile(np.arange(100.), p, interpolation="midpoint")
assert_array_equal(p, p0)
+ def test_quantile_monotonic(self):
+ # GH 14685
+ # test that the return value of quantile is monotonic if p0 is ordered
+ p0 = np.arange(0, 1, 0.01)
+ quantile = np.quantile(np.array([0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 1, 1, 9, 9, 9,
+ 8, 8, 7]) * 0.1, p0)
+ assert_equal(np.sort(quantile), quantile)
+
+ @hypothesis.given(arr=arrays(dtype=np.float, shape=st.integers(min_value=3,
+ max_value=1000),
+ elements=st.floats(allow_infinity=False,
+ allow_nan=False,
+ min_value=-1e300,
+ max_value=1e300)))
+ def test_quantile_monotonic_hypo(self, arr):
+ p0 = np.arange(0, 1, 0.01)
+ quantile = np.quantile(arr, p0)
+ assert_equal(np.sort(quantile), quantile)
+
+
+class TestLerp:
+ @hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False,
+ min_value=0, max_value=1),
+ t1=st.floats(allow_nan=False, allow_infinity=False,
+ min_value=0, max_value=1),
+ a = st.floats(allow_nan=False, allow_infinity=False,
+ 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)
+ if t0 == t1 or a == b:
+ assert l0 == l1 # uninteresting
+ elif (t0 < t1) == (a < b):
+ assert l0 <= l1
+ else:
+ assert l0 >= l1
+
+ @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False,
+ min_value=0, max_value=1),
+ a=st.floats(allow_nan=False, allow_infinity=False,
+ 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):
+ if a <= b:
+ assert a <= np.lib.function_base._lerp(a, b, t) <= b
+ else:
+ assert b <= np.lib.function_base._lerp(a, b, t) <= a
+
+ @hypothesis.given(t=st.floats(allow_nan=False, allow_infinity=False,
+ min_value=0, max_value=1),
+ a=st.floats(allow_nan=False, allow_infinity=False,
+ 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):
+ # double subtraction is needed to remove the extra precision that t < 0.5 has
+ assert np.lib.function_base._lerp(a, b, 1 - (1 - t)) == np.lib.function_base._lerp(b, a, 1 - t)
+
+ def test_lerp_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
+
class TestMedian: