summaryrefslogtreecommitdiff
path: root/numpy/lib/tests/test_function_base.py
diff options
context:
space:
mode:
authorscoder <stefan_ml@behnel.de>2023-05-04 09:29:53 +0200
committerGitHub <noreply@github.com>2023-05-04 09:29:53 +0200
commit442c8f48d3146ec32c7d5387310e171276cf10ac (patch)
treed8911d1a64e384b7955d3fc09a07edd218a9f1ee /numpy/lib/tests/test_function_base.py
parent3e4a6cba2da27bbe2a6e12c163238e503c9f6a07 (diff)
parent9163e933df91b516b6f0c7a9ba8ad1750e642f37 (diff)
downloadnumpy-442c8f48d3146ec32c7d5387310e171276cf10ac.tar.gz
Merge branch 'main' into cython3_noexcept
Diffstat (limited to 'numpy/lib/tests/test_function_base.py')
-rw-r--r--numpy/lib/tests/test_function_base.py917
1 files changed, 787 insertions, 130 deletions
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index eb2fc3311..b0944ec85 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -8,14 +8,14 @@ import pytest
import hypothesis
from hypothesis.extra.numpy import arrays
import hypothesis.strategies as st
-
+from functools import partial
import numpy as np
from numpy import ma
from numpy.testing import (
assert_, assert_equal, assert_array_equal, assert_almost_equal,
assert_array_almost_equal, assert_raises, assert_allclose, IS_PYPY,
- assert_warns, assert_raises_regex, suppress_warnings, HAS_REFCOUNT,
+ assert_warns, assert_raises_regex, suppress_warnings, HAS_REFCOUNT, IS_WASM
)
import numpy.lib.function_base as nfb
from numpy.random import rand
@@ -25,6 +25,7 @@ from numpy.lib import (
i0, insert, interp, kaiser, meshgrid, msort, piecewise, place, rot90,
select, setxor1d, sinc, trapz, trim_zeros, unwrap, unique, vectorize
)
+from numpy.core.numeric import normalize_axis_tuple
def get_mat(n):
@@ -228,8 +229,8 @@ class TestAny:
def test_nd(self):
y1 = [[0, 0, 0], [0, 1, 0], [1, 1, 0]]
assert_(np.any(y1))
- assert_array_equal(np.sometrue(y1, axis=0), [1, 1, 0])
- assert_array_equal(np.sometrue(y1, axis=1), [0, 1, 1])
+ assert_array_equal(np.any(y1, axis=0), [1, 1, 0])
+ assert_array_equal(np.any(y1, axis=1), [0, 1, 1])
class TestAll:
@@ -246,8 +247,8 @@ class TestAll:
def test_nd(self):
y1 = [[0, 0, 1], [0, 1, 1], [1, 1, 1]]
assert_(not np.all(y1))
- assert_array_equal(np.alltrue(y1, axis=0), [0, 0, 1])
- assert_array_equal(np.alltrue(y1, axis=1), [0, 0, 1])
+ assert_array_equal(np.all(y1, axis=0), [0, 0, 1])
+ assert_array_equal(np.all(y1, axis=1), [0, 0, 1])
class TestCopy:
@@ -305,6 +306,29 @@ class TestAverage:
assert_almost_equal(y5.mean(0), average(y5, 0))
assert_almost_equal(y5.mean(1), average(y5, 1))
+ @pytest.mark.parametrize(
+ 'x, axis, expected_avg, weights, expected_wavg, expected_wsum',
+ [([1, 2, 3], None, [2.0], [3, 4, 1], [1.75], [8.0]),
+ ([[1, 2, 5], [1, 6, 11]], 0, [[1.0, 4.0, 8.0]],
+ [1, 3], [[1.0, 5.0, 9.5]], [[4, 4, 4]])],
+ )
+ def test_basic_keepdims(self, x, axis, expected_avg,
+ weights, expected_wavg, expected_wsum):
+ avg = np.average(x, axis=axis, keepdims=True)
+ assert avg.shape == np.shape(expected_avg)
+ assert_array_equal(avg, expected_avg)
+
+ wavg = np.average(x, axis=axis, weights=weights, keepdims=True)
+ assert wavg.shape == np.shape(expected_wavg)
+ assert_array_equal(wavg, expected_wavg)
+
+ wavg, wsum = np.average(x, axis=axis, weights=weights, returned=True,
+ keepdims=True)
+ assert wavg.shape == np.shape(expected_wavg)
+ assert_array_equal(wavg, expected_wavg)
+ assert wsum.shape == np.shape(expected_wsum)
+ assert_array_equal(wsum, expected_wsum)
+
def test_weights(self):
y = np.arange(10)
w = np.arange(10)
@@ -337,6 +361,18 @@ class TestAverage:
assert_(np.average(y3, weights=w3).dtype == np.result_type(y3, w3))
+ # test weights with `keepdims=False` and `keepdims=True`
+ x = np.array([2, 3, 4]).reshape(3, 1)
+ w = np.array([4, 5, 6]).reshape(3, 1)
+
+ actual = np.average(x, weights=w, axis=1, keepdims=False)
+ desired = np.array([2., 3., 4.])
+ assert_array_equal(actual, desired)
+
+ actual = np.average(x, weights=w, axis=1, keepdims=True)
+ desired = np.array([[2.], [3.], [4.]])
+ assert_array_equal(actual, desired)
+
def test_returned(self):
y = np.array([[1, 2, 3], [4, 5, 6]])
@@ -386,6 +422,11 @@ class TestAverage:
w /= w.sum()
assert_almost_equal(a.mean(0), average(a, weights=w))
+ def test_average_class_without_dtype(self):
+ # see gh-21988
+ a = np.array([Fraction(1, 5), Fraction(3, 5)])
+ assert_equal(np.average(a), Fraction(2, 5))
+
class TestSelect:
choices = [np.array([1, 2, 3]),
np.array([4, 5, 6]),
@@ -553,6 +594,11 @@ class TestInsert:
with pytest.raises(IndexError):
np.insert([0, 1, 2], np.array([], dtype=float), [])
+ @pytest.mark.parametrize('idx', [4, -4])
+ def test_index_out_of_bounds(self, idx):
+ with pytest.raises(IndexError, match='out of bounds'):
+ np.insert([0, 1, 2], [idx], [3, 4])
+
class TestAmax:
@@ -805,7 +851,7 @@ class TestDiff:
class TestDelete:
- def setup(self):
+ def setup_method(self):
self.a = np.arange(5)
self.nd_a = np.arange(5).repeat(2).reshape(1, 5, 2)
@@ -885,6 +931,40 @@ class TestDelete:
with pytest.raises(IndexError):
np.delete([0, 1, 2], np.array([], dtype=float))
+ @pytest.mark.parametrize("indexer", [np.array([1]), [1]])
+ def test_single_item_array(self, indexer):
+ a_del_int = delete(self.a, 1)
+ a_del = delete(self.a, indexer)
+ assert_equal(a_del_int, a_del)
+
+ nd_a_del_int = delete(self.nd_a, 1, axis=1)
+ nd_a_del = delete(self.nd_a, np.array([1]), axis=1)
+ assert_equal(nd_a_del_int, nd_a_del)
+
+ def test_single_item_array_non_int(self):
+ # Special handling for integer arrays must not affect non-integer ones.
+ # If `False` was cast to `0` it would delete the element:
+ res = delete(np.ones(1), np.array([False]))
+ assert_array_equal(res, np.ones(1))
+
+ # Test the more complicated (with axis) case from gh-21840
+ x = np.ones((3, 1))
+ false_mask = np.array([False], dtype=bool)
+ true_mask = np.array([True], dtype=bool)
+
+ res = delete(x, false_mask, axis=-1)
+ assert_array_equal(res, x)
+ res = delete(x, true_mask, axis=-1)
+ assert_array_equal(res, x[:, :0])
+
+ # Object or e.g. timedeltas should *not* be allowed
+ with pytest.raises(IndexError):
+ delete(np.ones(2), np.array([0], dtype=object))
+
+ with pytest.raises(IndexError):
+ # timedeltas are sometimes "integral, but clearly not allowed:
+ delete(np.ones(2), np.array([0], dtype="m8[ns]"))
+
class TestGradient:
@@ -1137,6 +1217,13 @@ class TestGradient:
dfdx = gradient(f, x)
assert_array_equal(dfdx, [0.5, 0.5])
+ def test_return_type(self):
+ res = np.gradient(([1, 2], [2, 3]))
+ if np._using_numpy2_behavior():
+ assert type(res) is tuple
+ else:
+ assert type(res) is list
+
class TestAngle:
@@ -1166,25 +1253,67 @@ class TestAngle:
class TestTrimZeros:
- """
- Only testing for integer splits.
+ a = np.array([0, 0, 1, 0, 2, 3, 4, 0])
+ b = a.astype(float)
+ c = a.astype(complex)
+ d = a.astype(object)
- """
+ def values(self):
+ attr_names = ('a', 'b', 'c', 'd')
+ return (getattr(self, name) for name in attr_names)
def test_basic(self):
- a = np.array([0, 0, 1, 2, 3, 4, 0])
- res = trim_zeros(a)
- assert_array_equal(res, np.array([1, 2, 3, 4]))
+ slc = np.s_[2:-1]
+ for arr in self.values():
+ res = trim_zeros(arr)
+ assert_array_equal(res, arr[slc])
def test_leading_skip(self):
- a = np.array([0, 0, 1, 0, 2, 3, 4, 0])
- res = trim_zeros(a)
- assert_array_equal(res, np.array([1, 0, 2, 3, 4]))
+ slc = np.s_[:-1]
+ for arr in self.values():
+ res = trim_zeros(arr, trim='b')
+ assert_array_equal(res, arr[slc])
def test_trailing_skip(self):
- a = np.array([0, 0, 1, 0, 2, 3, 0, 4, 0])
- res = trim_zeros(a)
- assert_array_equal(res, np.array([1, 0, 2, 3, 0, 4]))
+ slc = np.s_[2:]
+ for arr in self.values():
+ res = trim_zeros(arr, trim='F')
+ assert_array_equal(res, arr[slc])
+
+ def test_all_zero(self):
+ for _arr in self.values():
+ arr = np.zeros_like(_arr, dtype=_arr.dtype)
+
+ res1 = trim_zeros(arr, trim='B')
+ assert len(res1) == 0
+
+ res2 = trim_zeros(arr, trim='f')
+ assert len(res2) == 0
+
+ def test_size_zero(self):
+ arr = np.zeros(0)
+ res = trim_zeros(arr)
+ assert_array_equal(arr, res)
+
+ @pytest.mark.parametrize(
+ 'arr',
+ [np.array([0, 2**62, 0]),
+ np.array([0, 2**63, 0]),
+ np.array([0, 2**64, 0])]
+ )
+ def test_overflow(self, arr):
+ slc = np.s_[1:2]
+ res = trim_zeros(arr)
+ assert_array_equal(res, arr[slc])
+
+ def test_no_trim(self):
+ arr = np.array([None, 1, None])
+ res = trim_zeros(arr)
+ assert_array_equal(arr, res)
+
+ def test_list_to_list(self):
+ res = trim_zeros(self.a.tolist())
+ assert isinstance(res, list)
class TestExtins:
@@ -1486,6 +1615,21 @@ class TestVectorize:
([('x',)], [('y',), ()]))
assert_equal(nfb._parse_gufunc_signature('(),(a,b,c),(d)->(d,e)'),
([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')]))
+
+ # Tests to check if whitespaces are ignored
+ assert_equal(nfb._parse_gufunc_signature('(x )->()'), ([('x',)], [()]))
+ assert_equal(nfb._parse_gufunc_signature('( x , y )->( )'),
+ ([('x', 'y')], [()]))
+ assert_equal(nfb._parse_gufunc_signature('(x),( y) ->()'),
+ ([('x',), ('y',)], [()]))
+ assert_equal(nfb._parse_gufunc_signature('( x)-> (y ) '),
+ ([('x',)], [('y',)]))
+ assert_equal(nfb._parse_gufunc_signature(' (x)->( y),( )'),
+ ([('x',)], [('y',), ()]))
+ assert_equal(nfb._parse_gufunc_signature(
+ '( ), ( a, b,c ) ,( d) -> (d , e)'),
+ ([(), ('a', 'b', 'c'), ('d',)], [('d', 'e')]))
+
with assert_raises(ValueError):
nfb._parse_gufunc_signature('(x)(y)->()')
with assert_raises(ValueError):
@@ -1623,6 +1767,90 @@ class TestVectorize:
with assert_raises_regex(ValueError, 'new output dimensions'):
f(x)
+ def test_subclasses(self):
+ class subclass(np.ndarray):
+ pass
+
+ m = np.array([[1., 0., 0.],
+ [0., 0., 1.],
+ [0., 1., 0.]]).view(subclass)
+ v = np.array([[1., 2., 3.], [4., 5., 6.], [7., 8., 9.]]).view(subclass)
+ # generalized (gufunc)
+ matvec = np.vectorize(np.matmul, signature='(m,m),(m)->(m)')
+ r = matvec(m, v)
+ assert_equal(type(r), subclass)
+ assert_equal(r, [[1., 3., 2.], [4., 6., 5.], [7., 9., 8.]])
+
+ # element-wise (ufunc)
+ mult = np.vectorize(lambda x, y: x*y)
+ r = mult(m, v)
+ assert_equal(type(r), subclass)
+ assert_equal(r, m * v)
+
+ def test_name(self):
+ #See gh-23021
+ @np.vectorize
+ def f2(a, b):
+ return a + b
+
+ assert f2.__name__ == 'f2'
+
+ def test_decorator(self):
+ @vectorize
+ def addsubtract(a, b):
+ if a > b:
+ return a - b
+ else:
+ return a + b
+
+ r = addsubtract([0, 3, 6, 9], [1, 3, 5, 7])
+ assert_array_equal(r, [1, 6, 1, 2])
+
+ def test_docstring(self):
+ @vectorize
+ def f(x):
+ """Docstring"""
+ return x
+
+ if sys.flags.optimize < 2:
+ assert f.__doc__ == "Docstring"
+
+ def test_partial(self):
+ def foo(x, y):
+ return x + y
+
+ bar = partial(foo, 3)
+ vbar = np.vectorize(bar)
+ assert vbar(1) == 4
+
+ def test_signature_otypes_decorator(self):
+ @vectorize(signature='(n)->(n)', otypes=['float64'])
+ def f(x):
+ return x
+
+ r = f([1, 2, 3])
+ assert_equal(r.dtype, np.dtype('float64'))
+ assert_array_equal(r, [1, 2, 3])
+ assert f.__name__ == 'f'
+
+ def test_bad_input(self):
+ with assert_raises(TypeError):
+ A = np.vectorize(pyfunc = 3)
+
+ def test_no_keywords(self):
+ with assert_raises(TypeError):
+ @np.vectorize("string")
+ def foo():
+ return "bar"
+
+ def test_positional_regression_9477(self):
+ # This supplies the first keyword argument as a positional,
+ # to ensure that they are still properly forwarded after the
+ # enhancement for #9477
+ f = vectorize((lambda x: x), ['float64'])
+ r = f([2])
+ assert_equal(r.dtype, np.dtype('float64'))
+
class TestLeaks:
class A:
@@ -1664,6 +1892,7 @@ class TestLeaks:
finally:
gc.enable()
+
class TestDigitize:
def test_forward(self):
@@ -1757,36 +1986,135 @@ class TestUnwrap:
# check that unwrap maintains continuity
assert_(np.all(diff(unwrap(rand(10) * 100)) < np.pi))
-
+ def test_period(self):
+ # check that unwrap removes jumps greater that 255
+ assert_array_equal(unwrap([1, 1 + 256], period=255), [1, 2])
+ # check that unwrap maintains continuity
+ assert_(np.all(diff(unwrap(rand(10) * 1000, period=255)) < 255))
+ # check simple case
+ simple_seq = np.array([0, 75, 150, 225, 300])
+ wrap_seq = np.mod(simple_seq, 255)
+ assert_array_equal(unwrap(wrap_seq, period=255), simple_seq)
+ # check custom discont value
+ uneven_seq = np.array([0, 75, 150, 225, 300, 430])
+ wrap_uneven = np.mod(uneven_seq, 250)
+ no_discont = unwrap(wrap_uneven, period=250)
+ assert_array_equal(no_discont, [0, 75, 150, 225, 300, 180])
+ sm_discont = unwrap(wrap_uneven, period=250, discont=140)
+ assert_array_equal(sm_discont, [0, 75, 150, 225, 300, 430])
+ assert sm_discont.dtype == wrap_uneven.dtype
+
+
+@pytest.mark.parametrize(
+ "dtype", "O" + np.typecodes["AllInteger"] + np.typecodes["Float"]
+)
+@pytest.mark.parametrize("M", [0, 1, 10])
class TestFilterwindows:
- def test_hanning(self):
+ def test_hanning(self, dtype: str, M: int) -> None:
+ scalar = np.array(M, dtype=dtype)[()]
+
+ w = hanning(scalar)
+ if dtype == "O":
+ ref_dtype = np.float64
+ else:
+ ref_dtype = np.result_type(scalar.dtype, np.float64)
+ assert w.dtype == ref_dtype
+
# check symmetry
- w = hanning(10)
- assert_array_almost_equal(w, flipud(w), 7)
+ assert_equal(w, flipud(w))
+
# check known value
- assert_almost_equal(np.sum(w, axis=0), 4.500, 4)
+ if scalar < 1:
+ assert_array_equal(w, np.array([]))
+ elif scalar == 1:
+ assert_array_equal(w, np.ones(1))
+ else:
+ assert_almost_equal(np.sum(w, axis=0), 4.500, 4)
+
+ def test_hamming(self, dtype: str, M: int) -> None:
+ scalar = np.array(M, dtype=dtype)[()]
+
+ w = hamming(scalar)
+ if dtype == "O":
+ ref_dtype = np.float64
+ else:
+ ref_dtype = np.result_type(scalar.dtype, np.float64)
+ assert w.dtype == ref_dtype
- def test_hamming(self):
# check symmetry
- w = hamming(10)
- assert_array_almost_equal(w, flipud(w), 7)
+ assert_equal(w, flipud(w))
+
# check known value
- assert_almost_equal(np.sum(w, axis=0), 4.9400, 4)
+ if scalar < 1:
+ assert_array_equal(w, np.array([]))
+ elif scalar == 1:
+ assert_array_equal(w, np.ones(1))
+ else:
+ assert_almost_equal(np.sum(w, axis=0), 4.9400, 4)
+
+ def test_bartlett(self, dtype: str, M: int) -> None:
+ scalar = np.array(M, dtype=dtype)[()]
+
+ w = bartlett(scalar)
+ if dtype == "O":
+ ref_dtype = np.float64
+ else:
+ ref_dtype = np.result_type(scalar.dtype, np.float64)
+ assert w.dtype == ref_dtype
- def test_bartlett(self):
# check symmetry
- w = bartlett(10)
- assert_array_almost_equal(w, flipud(w), 7)
+ assert_equal(w, flipud(w))
+
# check known value
- assert_almost_equal(np.sum(w, axis=0), 4.4444, 4)
+ if scalar < 1:
+ assert_array_equal(w, np.array([]))
+ elif scalar == 1:
+ assert_array_equal(w, np.ones(1))
+ else:
+ assert_almost_equal(np.sum(w, axis=0), 4.4444, 4)
+
+ def test_blackman(self, dtype: str, M: int) -> None:
+ scalar = np.array(M, dtype=dtype)[()]
+
+ w = blackman(scalar)
+ if dtype == "O":
+ ref_dtype = np.float64
+ else:
+ ref_dtype = np.result_type(scalar.dtype, np.float64)
+ assert w.dtype == ref_dtype
- def test_blackman(self):
# check symmetry
- w = blackman(10)
- assert_array_almost_equal(w, flipud(w), 7)
+ assert_equal(w, flipud(w))
+
# check known value
- assert_almost_equal(np.sum(w, axis=0), 3.7800, 4)
+ if scalar < 1:
+ assert_array_equal(w, np.array([]))
+ elif scalar == 1:
+ assert_array_equal(w, np.ones(1))
+ else:
+ assert_almost_equal(np.sum(w, axis=0), 3.7800, 4)
+
+ def test_kaiser(self, dtype: str, M: int) -> None:
+ scalar = np.array(M, dtype=dtype)[()]
+
+ w = kaiser(scalar, 0)
+ if dtype == "O":
+ ref_dtype = np.float64
+ else:
+ ref_dtype = np.result_type(scalar.dtype, np.float64)
+ assert w.dtype == ref_dtype
+
+ # check symmetry
+ assert_equal(w, flipud(w))
+
+ # check known value
+ if scalar < 1:
+ assert_array_equal(w, np.array([]))
+ elif scalar == 1:
+ assert_array_equal(w, np.ones(1))
+ else:
+ assert_almost_equal(np.sum(w, axis=0), 10, 15)
class TestTrapz:
@@ -1981,6 +2309,12 @@ class TestCorrCoef:
assert_array_almost_equal(c, np.array([[1., -1.], [-1., 1.]]))
assert_(np.all(np.abs(c) <= 1.0))
+ @pytest.mark.parametrize("test_type", [np.half, np.single, np.double, np.longdouble])
+ def test_corrcoef_dtype(self, test_type):
+ cast_A = self.A.astype(test_type)
+ res = corrcoef(cast_A, dtype=test_type)
+ assert test_type == res.dtype
+
class TestCov:
x1 = np.array([[0, 2], [1, 1], [2, 0]]).T
@@ -2081,6 +2415,12 @@ class TestCov:
aweights=self.unit_weights),
self.res1)
+ @pytest.mark.parametrize("test_type", [np.half, np.single, np.double, np.longdouble])
+ def test_cov_dtype(self, test_type):
+ cast_x1 = self.x1.astype(test_type)
+ res = cov(cast_x1, dtype=test_type)
+ assert test_type == res.dtype
+
class Test_I0:
@@ -2089,8 +2429,9 @@ class Test_I0:
i0(0.5),
np.array(1.0634833707413234))
- A = np.array([0.49842636, 0.6969809, 0.22011976, 0.0155549])
- expected = np.array([1.06307822, 1.12518299, 1.01214991, 1.00006049])
+ # need at least one test above 8, as the implementation is piecewise
+ A = np.array([0.49842636, 0.6969809, 0.22011976, 0.0155549, 10.0])
+ expected = np.array([1.06307822, 1.12518299, 1.01214991, 1.00006049, 2815.71662847])
assert_almost_equal(i0(A), expected)
assert_almost_equal(i0(-A), expected)
@@ -2127,6 +2468,11 @@ class Test_I0:
assert_array_equal(exp, res)
+ def test_complex(self):
+ a = np.array([0, 1 + 2j])
+ with pytest.raises(TypeError, match="i0 not supported for complex values"):
+ res = i0(a)
+
class TestKaiser:
@@ -2153,11 +2499,12 @@ class TestMsort:
A = np.array([[0.44567325, 0.79115165, 0.54900530],
[0.36844147, 0.37325583, 0.96098397],
[0.64864341, 0.52929049, 0.39172155]])
- assert_almost_equal(
- msort(A),
- np.array([[0.36844147, 0.37325583, 0.39172155],
- [0.44567325, 0.52929049, 0.54900530],
- [0.64864341, 0.79115165, 0.96098397]]))
+ with pytest.warns(DeprecationWarning, match="msort is deprecated"):
+ assert_almost_equal(
+ msort(A),
+ np.array([[0.36844147, 0.37325583, 0.39172155],
+ [0.44567325, 0.52929049, 0.54900530],
+ [0.64864341, 0.79115165, 0.96098397]]))
class TestMeshgrid:
@@ -2248,6 +2595,27 @@ class TestMeshgrid:
assert_equal(x[0, :], 0)
assert_equal(x[1, :], X)
+ def test_nd_shape(self):
+ a, b, c, d, e = np.meshgrid(*([0] * i for i in range(1, 6)))
+ expected_shape = (2, 1, 3, 4, 5)
+ assert_equal(a.shape, expected_shape)
+ assert_equal(b.shape, expected_shape)
+ assert_equal(c.shape, expected_shape)
+ assert_equal(d.shape, expected_shape)
+ assert_equal(e.shape, expected_shape)
+
+ def test_nd_values(self):
+ a, b, c = np.meshgrid([0], [1, 2], [3, 4, 5])
+ assert_equal(a, [[[0, 0, 0]], [[0, 0, 0]]])
+ assert_equal(b, [[[1, 1, 1]], [[2, 2, 2]]])
+ assert_equal(c, [[[3, 4, 5]], [[3, 4, 5]]])
+
+ def test_nd_indexing(self):
+ a, b, c = np.meshgrid([0], [1, 2], [3, 4, 5], indexing='ij')
+ assert_equal(a, [[[0, 0, 0], [0, 0, 0]]])
+ assert_equal(b, [[[1, 1, 1], [2, 2, 2]]])
+ assert_equal(c, [[[3, 4, 5], [3, 4, 5]]])
+
class TestPiecewise:
@@ -2340,6 +2708,14 @@ class TestPiecewise:
assert_array_equal(y, np.array([[-1., -1., -1.],
[3., 3., 1.]]))
+ def test_subclasses(self):
+ class subclass(np.ndarray):
+ pass
+ x = np.arange(5.).view(subclass)
+ r = piecewise(x, [x<2., x>=4], [-1., 1., 0.])
+ assert_equal(type(r), subclass)
+ assert_equal(r, [-1., -1., 0., 0., 1.])
+
class TestBincount:
@@ -2631,11 +3007,6 @@ class TestInterp:
assert_almost_equal(np.interp(x, xp, fp, period=360), y)
-def compare_results(res, desired):
- for i in range(len(desired)):
- assert_array_equal(res[i], desired[i])
-
-
class TestPercentile:
def test_basic(self):
@@ -2645,7 +3016,7 @@ class TestPercentile:
assert_equal(np.percentile(x, 50), 1.75)
x[1] = np.nan
assert_equal(np.percentile(x, 0), np.nan)
- assert_equal(np.percentile(x, 0, interpolation='nearest'), np.nan)
+ assert_equal(np.percentile(x, 0, method='nearest'), np.nan)
def test_fraction(self):
x = [Fraction(i, 2) for i in range(8)]
@@ -2662,6 +3033,10 @@ class TestPercentile:
assert_equal(p, Fraction(7, 4))
assert_equal(type(p), Fraction)
+ p = np.percentile(x, [Fraction(50)])
+ assert_equal(p, np.array([Fraction(7, 4)]))
+ assert_equal(type(p), np.ndarray)
+
def test_api(self):
d = np.ones(5)
np.percentile(d, 5, None, None, False)
@@ -2669,6 +3044,14 @@ class TestPercentile:
o = np.ones((1,))
np.percentile(d, 5, None, o, False, 'linear')
+ def test_complex(self):
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='G')
+ assert_raises(TypeError, np.percentile, arr_c, 0.5)
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='D')
+ assert_raises(TypeError, np.percentile, arr_c, 0.5)
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='F')
+ assert_raises(TypeError, np.percentile, arr_c, 0.5)
+
def test_2D(self):
x = np.array([[1, 1, 1],
[1, 1, 1],
@@ -2677,36 +3060,97 @@ class TestPercentile:
[1, 1, 1]])
assert_array_equal(np.percentile(x, 50, axis=0), [1, 1, 1])
- def test_linear(self):
-
- # 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,
- interpolation='lower'), 4)
- assert_equal(np.percentile(range(10), 50,
- interpolation='higher'), 5)
-
- def test_midpoint(self):
- assert_equal(np.percentile(range(10), 51,
- interpolation='midpoint'), 4.5)
- assert_equal(np.percentile(range(11), 51,
- interpolation='midpoint'), 5.5)
- assert_equal(np.percentile(range(11), 50,
- interpolation='midpoint'), 5)
-
- def test_nearest(self):
- assert_equal(np.percentile(range(10), 51,
- interpolation='nearest'), 5)
- assert_equal(np.percentile(range(10), 49,
- interpolation='nearest'), 4)
+ @pytest.mark.parametrize("dtype", np.typecodes["Float"])
+ 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,
+ method="linear")
+ np.testing.assert_equal(res, np.NAN)
+ np.testing.assert_equal(res.dtype, arr.dtype)
+
+ H_F_TYPE_CODES = [(int_type, np.float64)
+ for int_type in np.typecodes["AllInteger"]
+ ] + [(np.float16, np.float16),
+ (np.float32, np.float32),
+ (np.float64, np.float64),
+ (np.longdouble, np.longdouble),
+ (np.dtype("O"), np.float64)]
+
+ @pytest.mark.parametrize(["input_dtype", "expected_dtype"], H_F_TYPE_CODES)
+ @pytest.mark.parametrize(["method", "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,
+ method,
+ expected,
+ input_dtype,
+ expected_dtype):
+ expected_dtype = np.dtype(expected_dtype)
+ if np._get_promotion_state() == "legacy":
+ expected_dtype = np.promote_types(expected_dtype, np.float64)
+
+ arr = np.asarray([15.0, 20.0, 35.0, 40.0, 50.0], dtype=input_dtype)
+ actual = np.percentile(arr, 40.0, method=method)
+
+ np.testing.assert_almost_equal(
+ actual, expected_dtype.type(expected), 14)
+
+ if method 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))
+
+ TYPE_CODES = np.typecodes["AllInteger"] + np.typecodes["Float"] + "O"
+
+ @pytest.mark.parametrize("dtype", TYPE_CODES)
+ def test_lower_higher(self, dtype):
+ assert_equal(np.percentile(np.arange(10, dtype=dtype), 50,
+ method='lower'), 4)
+ assert_equal(np.percentile(np.arange(10, dtype=dtype), 50,
+ method='higher'), 5)
+
+ @pytest.mark.parametrize("dtype", TYPE_CODES)
+ def test_midpoint(self, dtype):
+ assert_equal(np.percentile(np.arange(10, dtype=dtype), 51,
+ method='midpoint'), 4.5)
+ assert_equal(np.percentile(np.arange(9, dtype=dtype) + 1, 50,
+ method='midpoint'), 5)
+ assert_equal(np.percentile(np.arange(11, dtype=dtype), 51,
+ method='midpoint'), 5.5)
+ assert_equal(np.percentile(np.arange(11, dtype=dtype), 50,
+ method='midpoint'), 5)
+
+ @pytest.mark.parametrize("dtype", TYPE_CODES)
+ def test_nearest(self, dtype):
+ assert_equal(np.percentile(np.arange(10, dtype=dtype), 51,
+ method='nearest'), 5)
+ assert_equal(np.percentile(np.arange(10, dtype=dtype), 49,
+ method='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
@@ -2734,19 +3178,19 @@ class TestPercentile:
assert_equal(
np.percentile(x, (25, 50, 75), axis=1).shape, (3, 3, 5, 6))
assert_equal(np.percentile(x, (25, 50),
- interpolation="higher").shape, (2,))
+ method="higher").shape, (2,))
assert_equal(np.percentile(x, (25, 50, 75),
- interpolation="higher").shape, (3,))
+ method="higher").shape, (3,))
assert_equal(np.percentile(x, (25, 50), axis=0,
- interpolation="higher").shape, (2, 4, 5, 6))
+ method="higher").shape, (2, 4, 5, 6))
assert_equal(np.percentile(x, (25, 50), axis=1,
- interpolation="higher").shape, (2, 3, 5, 6))
+ method="higher").shape, (2, 3, 5, 6))
assert_equal(np.percentile(x, (25, 50), axis=2,
- interpolation="higher").shape, (2, 3, 4, 6))
+ method="higher").shape, (2, 3, 4, 6))
assert_equal(np.percentile(x, (25, 50), axis=3,
- interpolation="higher").shape, (2, 3, 4, 5))
+ method="higher").shape, (2, 3, 4, 5))
assert_equal(np.percentile(x, (25, 50, 75), axis=1,
- interpolation="higher").shape, (3, 3, 5, 6))
+ method="higher").shape, (3, 3, 5, 6))
def test_scalar_q(self):
# test for no empty dimensions for compatibility with old percentile
@@ -2772,33 +3216,33 @@ class TestPercentile:
# test for no empty dimensions for compatibility with old percentile
x = np.arange(12).reshape(3, 4)
- assert_equal(np.percentile(x, 50, interpolation='lower'), 5.)
+ assert_equal(np.percentile(x, 50, method='lower'), 5.)
assert_(np.isscalar(np.percentile(x, 50)))
r0 = np.array([4., 5., 6., 7.])
- c0 = np.percentile(x, 50, interpolation='lower', axis=0)
+ c0 = np.percentile(x, 50, method='lower', axis=0)
assert_equal(c0, r0)
assert_equal(c0.shape, r0.shape)
r1 = np.array([1., 5., 9.])
- c1 = np.percentile(x, 50, interpolation='lower', axis=1)
+ c1 = np.percentile(x, 50, method='lower', axis=1)
assert_almost_equal(c1, r1)
assert_equal(c1.shape, r1.shape)
out = np.empty((), dtype=x.dtype)
- c = np.percentile(x, 50, interpolation='lower', out=out)
+ c = np.percentile(x, 50, method='lower', out=out)
assert_equal(c, 5)
assert_equal(out, 5)
out = np.empty(4, dtype=x.dtype)
- c = np.percentile(x, 50, interpolation='lower', axis=0, out=out)
+ c = np.percentile(x, 50, method='lower', axis=0, out=out)
assert_equal(c, r0)
assert_equal(out, r0)
out = np.empty(3, dtype=x.dtype)
- c = np.percentile(x, 50, interpolation='lower', axis=1, out=out)
+ c = np.percentile(x, 50, method='lower', axis=1, out=out)
assert_equal(c, r1)
assert_equal(out, r1)
def test_exception(self):
assert_raises(ValueError, np.percentile, [1, 2], 56,
- interpolation='foobar')
+ method='foobar')
assert_raises(ValueError, np.percentile, [1], 101)
assert_raises(ValueError, np.percentile, [1], -1)
assert_raises(ValueError, np.percentile, [1], list(range(50)) + [101])
@@ -2812,18 +3256,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
@@ -2839,12 +3283,12 @@ class TestPercentile:
# q.dim > 1, int
r0 = np.array([[0, 1, 2, 3], [4, 5, 6, 7]])
out = np.empty((2, 4), dtype=x.dtype)
- c = np.percentile(x, (25, 50), interpolation='lower', axis=0, out=out)
+ c = np.percentile(x, (25, 50), method='lower', axis=0, out=out)
assert_equal(c, r0)
assert_equal(out, r0)
r1 = np.array([[0, 4, 8], [1, 5, 9]])
out = np.empty((2, 3), dtype=x.dtype)
- c = np.percentile(x, (25, 50), interpolation='lower', axis=1, out=out)
+ c = np.percentile(x, (25, 50), method='lower', axis=1, out=out)
assert_equal(c, r1)
assert_equal(out, r1)
@@ -2861,10 +3305,10 @@ class TestPercentile:
assert_array_equal(np.percentile(d, 50, axis=-4).shape, (1, 2, 1))
assert_array_equal(np.percentile(d, 50, axis=2,
- interpolation='midpoint').shape,
+ method='midpoint').shape,
(11, 1, 1))
assert_array_equal(np.percentile(d, 50, axis=-2,
- interpolation='midpoint').shape,
+ method='midpoint').shape,
(11, 1, 1))
assert_array_equal(np.array(np.percentile(d, [10, 50], axis=0)).shape,
@@ -2887,10 +3331,10 @@ class TestPercentile:
def test_no_p_overwrite(self):
p = np.linspace(0., 100., num=5)
- np.percentile(np.arange(100.), p, interpolation="midpoint")
+ np.percentile(np.arange(100.), p, method="midpoint")
assert_array_equal(p, np.linspace(0., 100., num=5))
p = np.linspace(0., 100., num=5).tolist()
- np.percentile(np.arange(100.), p, interpolation="midpoint")
+ np.percentile(np.arange(100.), p, method="midpoint")
assert_array_equal(p, np.linspace(0., 100., num=5).tolist())
def test_percentile_overwrite(self):
@@ -2964,18 +3408,44 @@ class TestPercentile:
assert_equal(np.percentile(d, [1, 7], axis=(0, 3),
keepdims=True).shape, (2, 1, 5, 7, 1))
+ @pytest.mark.parametrize('q', [7, [1, 7]])
+ @pytest.mark.parametrize(
+ argnames='axis',
+ argvalues=[
+ None,
+ 1,
+ (1,),
+ (0, 1),
+ (-3, -1),
+ ]
+ )
+ def test_keepdims_out(self, q, axis):
+ d = np.ones((3, 5, 7, 11))
+ if axis is None:
+ shape_out = (1,) * d.ndim
+ else:
+ axis_norm = normalize_axis_tuple(axis, d.ndim)
+ shape_out = tuple(
+ 1 if i in axis_norm else d.shape[i] for i in range(d.ndim))
+ shape_out = np.shape(q) + shape_out
+
+ out = np.empty(shape_out)
+ result = np.percentile(d, q, axis=axis, keepdims=True, out=out)
+ assert result is out
+ assert_equal(result.shape, shape_out)
+
def test_out(self):
o = np.zeros((4,))
d = np.ones((3, 4))
assert_equal(np.percentile(d, 0, 0, out=o), o)
- assert_equal(np.percentile(d, 0, 0, interpolation='nearest', out=o), o)
+ assert_equal(np.percentile(d, 0, 0, method='nearest', out=o), o)
o = np.zeros((3,))
assert_equal(np.percentile(d, 1, 1, out=o), o)
- assert_equal(np.percentile(d, 1, 1, interpolation='nearest', out=o), o)
+ assert_equal(np.percentile(d, 1, 1, method='nearest', out=o), o)
o = np.zeros(())
assert_equal(np.percentile(d, 2, out=o), o)
- assert_equal(np.percentile(d, 2, interpolation='nearest', out=o), o)
+ assert_equal(np.percentile(d, 2, method='nearest', out=o), o)
def test_out_nan(self):
with warnings.catch_warnings(record=True):
@@ -2985,15 +3455,15 @@ class TestPercentile:
d[2, 1] = np.nan
assert_equal(np.percentile(d, 0, 0, out=o), o)
assert_equal(
- np.percentile(d, 0, 0, interpolation='nearest', out=o), o)
+ np.percentile(d, 0, 0, method='nearest', out=o), o)
o = np.zeros((3,))
assert_equal(np.percentile(d, 1, 1, out=o), o)
assert_equal(
- np.percentile(d, 1, 1, interpolation='nearest', out=o), o)
+ np.percentile(d, 1, 1, method='nearest', out=o), o)
o = np.zeros(())
assert_equal(np.percentile(d, 1, out=o), o)
assert_equal(
- np.percentile(d, 1, interpolation='nearest', out=o), o)
+ np.percentile(d, 1, method='nearest', out=o), o)
def test_nan_behavior(self):
a = np.arange(24, dtype=float)
@@ -3048,18 +3518,48 @@ class TestPercentile:
b[:, 1] = np.nan
b[:, 2] = np.nan
assert_equal(np.percentile(a, [0.3, 0.6], (0, 2)), b)
- # axis02 not zerod with nearest interpolation
+ # axis02 not zerod with method='nearest'
b = np.percentile(np.arange(24, dtype=float).reshape(2, 3, 4),
- [0.3, 0.6], (0, 2), interpolation='nearest')
+ [0.3, 0.6], (0, 2), method='nearest')
b[:, 1] = np.nan
b[:, 2] = np.nan
assert_equal(np.percentile(
- a, [0.3, 0.6], (0, 2), interpolation='nearest'), b)
+ a, [0.3, 0.6], (0, 2), method='nearest'), b)
+
+ def test_nan_q(self):
+ # GH18830
+ with pytest.raises(ValueError, match="Percentiles must be in"):
+ np.percentile([1, 2, 3, 4.0], np.nan)
+ with pytest.raises(ValueError, match="Percentiles must be in"):
+ np.percentile([1, 2, 3, 4.0], [np.nan])
+ q = np.linspace(1.0, 99.0, 16)
+ q[0] = np.nan
+ with pytest.raises(ValueError, match="Percentiles must be in"):
+ np.percentile([1, 2, 3, 4.0], q)
+
+
+quantile_methods = [
+ 'inverted_cdf', 'averaged_inverted_cdf', 'closest_observation',
+ 'interpolated_inverted_cdf', 'hazen', 'weibull', 'linear',
+ 'median_unbiased', 'normal_unbiased', 'nearest', 'lower', 'higher',
+ 'midpoint']
class TestQuantile:
# most of this is already tested by TestPercentile
+ def V(self, x, y, alpha):
+ # Identification function used in several tests.
+ return (x >= y) - alpha
+
+ 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.)
@@ -3074,12 +3574,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)
@@ -3092,28 +3591,57 @@ class TestQuantile:
assert_equal(q, Fraction(7, 4))
assert_equal(type(q), Fraction)
+ q = np.quantile(x, [Fraction(1, 2)])
+ assert_equal(q, np.array([Fraction(7, 4)]))
+ assert_equal(type(q), np.ndarray)
+
+ q = np.quantile(x, [[Fraction(1, 2)]])
+ assert_equal(q, np.array([[Fraction(7, 4)]]))
+ assert_equal(type(q), np.ndarray)
+
# repeat with integral input but fractional quantile
x = np.arange(8)
assert_equal(np.quantile(x, Fraction(1, 2)), Fraction(7, 2))
+ def test_complex(self):
+ #See gh-22652
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='G')
+ assert_raises(TypeError, np.quantile, arr_c, 0.5)
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='D')
+ assert_raises(TypeError, np.quantile, arr_c, 0.5)
+ arr_c = np.array([0.5+3.0j, 2.1+0.5j, 1.6+2.3j], dtype='F')
+ assert_raises(TypeError, np.quantile, arr_c, 0.5)
+
def test_no_p_overwrite(self):
# this is worth retesting, because quantile does not make a copy
p0 = np.array([0, 0.75, 0.25, 0.5, 1.0])
p = p0.copy()
- np.quantile(np.arange(100.), p, interpolation="midpoint")
+ np.quantile(np.arange(100.), p, method="midpoint")
assert_array_equal(p, p0)
p0 = p0.tolist()
p = p.tolist()
- np.quantile(np.arange(100.), p, interpolation="midpoint")
+ np.quantile(np.arange(100.), p, method="midpoint")
assert_array_equal(p, p0)
- def test_quantile_monotonic(self):
+ @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],
+ method="nearest")
+ assert res.dtype == dtype
+
+ @pytest.mark.parametrize("method", quantile_methods)
+ def test_quantile_monotonic(self, method):
# GH 14685
# test that the return value of quantile is monotonic if p0 is ordered
- p0 = np.arange(0, 1, 0.01)
+ # Also tests that the boundary values are not mishandled.
+ p0 = np.linspace(0, 1, 101)
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)
+ 8, 8, 7]) * 0.1, p0, method=method)
+ assert_equal(np.sort(quantile), quantile)
+
+ # Also test one where the number of data points is clearly divisible:
+ quantile = np.quantile([0., 1., 2., 3.], p0, method=method)
assert_equal(np.sort(quantile), quantile)
@hypothesis.given(
@@ -3126,6 +3654,101 @@ 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)
+
+ @pytest.mark.parametrize("method", quantile_methods)
+ @pytest.mark.parametrize("alpha", [0.2, 0.5, 0.9])
+ def test_quantile_identification_equation(self, method, alpha):
+ # Test that the identification equation holds for the empirical
+ # CDF:
+ # E[V(x, Y)] = 0 <=> x is quantile
+ # with Y the random variable for which we have observed values and
+ # V(x, y) the canonical identification function for the quantile (at
+ # level alpha), see
+ # https://doi.org/10.48550/arXiv.0912.0902
+ rng = np.random.default_rng(4321)
+ # We choose n and alpha such that we cover 3 cases:
+ # - n * alpha is an integer
+ # - n * alpha is a float that gets rounded down
+ # - n * alpha is a float that gest rounded up
+ n = 102 # n * alpha = 20.4, 51. , 91.8
+ y = rng.random(n)
+ x = np.quantile(y, alpha, method=method)
+ if method in ("higher",):
+ # These methods do not fulfill the identification equation.
+ assert np.abs(np.mean(self.V(x, y, alpha))) > 0.1 / n
+ elif int(n * alpha) == n * alpha:
+ # We can expect exact results, up to machine precision.
+ assert_allclose(np.mean(self.V(x, y, alpha)), 0, atol=1e-14)
+ else:
+ # V = (x >= y) - alpha cannot sum to zero exactly but within
+ # "sample precision".
+ assert_allclose(np.mean(self.V(x, y, alpha)), 0,
+ atol=1 / n / np.amin([alpha, 1 - alpha]))
+
+ @pytest.mark.parametrize("method", quantile_methods)
+ @pytest.mark.parametrize("alpha", [0.2, 0.5, 0.9])
+ def test_quantile_add_and_multiply_constant(self, method, alpha):
+ # Test that
+ # 1. quantile(c + x) = c + quantile(x)
+ # 2. quantile(c * x) = c * quantile(x)
+ # 3. quantile(-x) = -quantile(x, 1 - alpha)
+ # On empirical quantiles, this equation does not hold exactly.
+ # Koenker (2005) "Quantile Regression" Chapter 2.2.3 calls these
+ # properties equivariance.
+ rng = np.random.default_rng(4321)
+ # We choose n and alpha such that we have cases for
+ # - n * alpha is an integer
+ # - n * alpha is a float that gets rounded down
+ # - n * alpha is a float that gest rounded up
+ n = 102 # n * alpha = 20.4, 51. , 91.8
+ y = rng.random(n)
+ q = np.quantile(y, alpha, method=method)
+ c = 13.5
+
+ # 1
+ assert_allclose(np.quantile(c + y, alpha, method=method), c + q)
+ # 2
+ assert_allclose(np.quantile(c * y, alpha, method=method), c * q)
+ # 3
+ q = -np.quantile(-y, 1 - alpha, method=method)
+ if method == "inverted_cdf":
+ if (
+ n * alpha == int(n * alpha)
+ or np.round(n * alpha) == int(n * alpha) + 1
+ ):
+ assert_allclose(q, np.quantile(y, alpha, method="higher"))
+ else:
+ assert_allclose(q, np.quantile(y, alpha, method="lower"))
+ elif method == "closest_observation":
+ if n * alpha == int(n * alpha):
+ assert_allclose(q, np.quantile(y, alpha, method="higher"))
+ elif np.round(n * alpha) == int(n * alpha) + 1:
+ assert_allclose(
+ q, np.quantile(y, alpha + 1/n, method="higher"))
+ else:
+ assert_allclose(q, np.quantile(y, alpha, method="lower"))
+ elif method == "interpolated_inverted_cdf":
+ assert_allclose(q, np.quantile(y, alpha + 1/n, method=method))
+ elif method == "nearest":
+ if n * alpha == int(n * alpha):
+ assert_allclose(q, np.quantile(y, alpha + 1/n, method=method))
+ else:
+ assert_allclose(q, np.quantile(y, alpha, method=method))
+ elif method == "lower":
+ assert_allclose(q, np.quantile(y, alpha, method="higher"))
+ elif method == "higher":
+ assert_allclose(q, np.quantile(y, alpha, method="lower"))
+ else:
+ # "averaged_inverted_cdf", "hazen", "weibull", "linear",
+ # "median_unbiased", "normal_unbiased", "midpoint"
+ assert_allclose(q, np.quantile(y, alpha, method=method))
+
class TestLerp:
@hypothesis.given(t0=st.floats(allow_nan=False, allow_infinity=False,
@@ -3136,9 +3759,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._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):
@@ -3152,11 +3775,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._lerp(a, b, t) <= b
else:
- assert b <= np.lib.function_base._lerp(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),
@@ -3164,17 +3787,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)
- assert left == right
+ left = nfb._lerp(a, b, 1 - (1 - t))
+ right = nfb._lerp(b, a, 1 - t)
+ assert_allclose(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._lerp(a, b, t) == 2.6
class TestMedian:
@@ -3274,6 +3897,16 @@ class TestMedian:
a = MySubClass([1, 2, 3])
assert_equal(np.median(a), -7)
+ @pytest.mark.parametrize('arr',
+ ([1., 2., 3.], [1., np.nan, 3.], np.nan, 0.))
+ def test_subclass2(self, arr):
+ """Check that we return subclasses, even if a NaN scalar."""
+ class MySubclass(np.ndarray):
+ pass
+
+ m = np.median(np.array(arr).view(MySubclass))
+ assert isinstance(m, MySubclass)
+
def test_out(self):
o = np.zeros((4,))
d = np.ones((3, 4))
@@ -3327,6 +3960,7 @@ class TestMedian:
b[2] = np.nan
assert_equal(np.median(a, (0, 2)), b)
+ @pytest.mark.skipif(IS_WASM, reason="fp errors don't work correctly")
def test_empty(self):
# mean(empty array) emits two warnings: empty slice and divide by 0
a = np.array([], dtype=float)
@@ -3415,6 +4049,29 @@ class TestMedian:
assert_equal(np.median(d, axis=(0, 1, 3), keepdims=True).shape,
(1, 1, 7, 1))
+ @pytest.mark.parametrize(
+ argnames='axis',
+ argvalues=[
+ None,
+ 1,
+ (1, ),
+ (0, 1),
+ (-3, -1),
+ ]
+ )
+ def test_keepdims_out(self, axis):
+ d = np.ones((3, 5, 7, 11))
+ if axis is None:
+ shape_out = (1,) * d.ndim
+ else:
+ axis_norm = normalize_axis_tuple(axis, d.ndim)
+ shape_out = tuple(
+ 1 if i in axis_norm else d.shape[i] for i in range(d.ndim))
+ out = np.empty(shape_out)
+ result = np.median(d, axis=axis, keepdims=True, out=out)
+ assert result is out
+ assert_equal(result.shape, shape_out)
+
class TestAdd_newdoc_ufunc: