summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEric Wieser <wieser.eric@gmail.com>2020-03-25 04:59:27 +0000
committerGitHub <noreply@github.com>2020-03-25 00:59:27 -0400
commitb03c575834a68f3a8dad5e2974ccee7fa76003ac (patch)
tree548c378c3fa17186f59e0c149b93300da92746ab
parentfed47a489e3da26fffb98a6dda32dbc9ba0f63c3 (diff)
downloadnumpy-b03c575834a68f3a8dad5e2974ccee7fa76003ac.tar.gz
DEP: Do not cast boolean indices to integers in np.delete (#15815)
This expires a deprecation from 1.8. The corresponding deprecation in `np.insert` has less clear semantics, so has been left to a future patch. Co-authored-by: Sebastian Berg <sebastian@sipsolutions.net> Co-authored-by: Warren Weckesser <warren.weckesser@gmail.com>
-rw-r--r--doc/release/upcoming_changes/15815.expired.rst6
-rw-r--r--numpy/lib/function_base.py42
-rw-r--r--numpy/lib/tests/test_function_base.py23
3 files changed, 41 insertions, 30 deletions
diff --git a/doc/release/upcoming_changes/15815.expired.rst b/doc/release/upcoming_changes/15815.expired.rst
new file mode 100644
index 000000000..b853cc9e5
--- /dev/null
+++ b/doc/release/upcoming_changes/15815.expired.rst
@@ -0,0 +1,6 @@
+`numpy.delete` no longer casts boolean indices to integers
+----------------------------------------------------------
+This concludes a deprecation from 1.8, where ``np.delete`` would cast boolean
+arrays and scalars passed as an index argument into integer indices. The
+behavior now is to treat boolean arrays as a mask, and to raise an error
+on boolean scalars.
diff --git a/numpy/lib/function_base.py b/numpy/lib/function_base.py
index 7211b68cf..bcf7898ef 100644
--- a/numpy/lib/function_base.py
+++ b/numpy/lib/function_base.py
@@ -4207,12 +4207,17 @@ def delete(arr, obj, axis=None):
Parameters
----------
arr : array_like
- Input array.
+ Input array.
obj : slice, int or array of ints
- Indicate indices of sub-arrays to remove along the specified axis.
+ Indicate indices of sub-arrays to remove along the specified axis.
+
+ .. versionchanged:: 1.19.0
+ Boolean indices are now treated as a mask of elements to remove,
+ rather than being cast to the integers 0 and 1.
+
axis : int, optional
- The axis along which to delete the subarray defined by `obj`.
- If `axis` is None, `obj` is applied to the flattened array.
+ The axis along which to delete the subarray defined by `obj`.
+ If `axis` is None, `obj` is applied to the flattened array.
Returns
-------
@@ -4330,19 +4335,8 @@ def delete(arr, obj, axis=None):
else:
return new
- _obj = obj
- obj = np.asarray(obj)
- # After removing the special handling of booleans and out of
- # bounds values, the conversion to the array can be removed.
- if obj.dtype == bool:
- # 2012-10-11, NumPy 1.8
- warnings.warn("in the future insert will treat boolean arrays and "
- "array-likes as boolean index instead of casting it "
- "to integer", FutureWarning, stacklevel=3)
- obj = obj.astype(intp)
- if isinstance(_obj, (int, long, integer)):
+ if isinstance(obj, (int, integer)) and not isinstance(obj, bool):
# optimization for a single value
- obj = obj.item()
if (obj < -N or obj >= N):
raise IndexError(
"index %i is out of bounds for axis %i with "
@@ -4358,11 +4352,23 @@ def delete(arr, obj, axis=None):
slobj2[axis] = slice(obj+1, None)
new[tuple(slobj)] = arr[tuple(slobj2)]
else:
+ _obj = obj
+ obj = np.asarray(obj)
if obj.size == 0 and not isinstance(_obj, np.ndarray):
obj = obj.astype(intp)
- keep = ones(N, dtype=bool)
- keep[obj, ] = False
+ if obj.dtype == bool:
+ if obj.shape != (N,):
+ raise ValueError('boolean array argument obj to delete '
+ 'must be one dimensional and match the axis '
+ 'length of {}'.format(N))
+
+ # optimization, the other branch is slower
+ keep = ~obj
+ else:
+ keep = ones(N, dtype=bool)
+ keep[obj,] = False
+
slobj[axis] = keep
new = arr[tuple(slobj)]
diff --git a/numpy/lib/tests/test_function_base.py b/numpy/lib/tests/test_function_base.py
index fb10205d4..3e621f077 100644
--- a/numpy/lib/tests/test_function_base.py
+++ b/numpy/lib/tests/test_function_base.py
@@ -809,9 +809,6 @@ class TestDelete:
a_del = delete(self.a, indices)
nd_a_del = delete(self.nd_a, indices, axis=1)
msg = 'Delete failed for obj: %r' % indices
- # NOTE: The cast should be removed after warning phase for bools
- if not isinstance(indices, (slice, int, long, np.integer)):
- indices = np.asarray(indices, dtype=np.intp)
assert_array_equal(setxor1d(a_del, self.a[indices, ]), self.a,
err_msg=msg)
xor = setxor1d(nd_a_del[0,:, 0], self.nd_a[0, indices, 0])
@@ -827,7 +824,6 @@ class TestDelete:
self._check_inverse_of_slicing(s)
def test_fancy(self):
- # Deprecation/FutureWarning tests should be kept after change.
self._check_inverse_of_slicing(np.array([[0, 1], [2, 1]]))
with pytest.raises(IndexError):
delete(self.a, [100])
@@ -836,14 +832,17 @@ class TestDelete:
self._check_inverse_of_slicing([0, -1, 2, 2])
- with warnings.catch_warnings(record=True) as w:
- warnings.filterwarnings('always', category=FutureWarning)
- obj = np.array([True, False, False], dtype=bool)
- self._check_inverse_of_slicing(obj)
- # _check_inverse_of_slicing operates on two arrays, so warns twice
- assert len(w) == 2
- assert_(w[0].category is FutureWarning)
- assert_(w[1].category is FutureWarning)
+ self._check_inverse_of_slicing([True, False, False, True, False])
+
+ # not legal, indexing with these would change the dimension
+ with pytest.raises(ValueError):
+ delete(self.a, True)
+ with pytest.raises(ValueError):
+ delete(self.a, False)
+
+ # not enough items
+ with pytest.raises(ValueError):
+ delete(self.a, [False]*4)
def test_single(self):
self._check_inverse_of_slicing(0)