8000 DEP: Deprecate non-tuple multidimensional indices · numpy/numpy@175e0f1 · GitHub
[go: up one dir, main page]

Skip to content

Commit 175e0f1

Browse files
sebergeric-wieser
authored andcommitted
DEP: Deprecate non-tuple multidimensional indices
Such indexing has problem with ambiguouity for example whether `arr[[[0], [1]]]` is `arr[[0], [1]]` or `arr[asarray([[0], [1]])]` Adds the tuple cast where it is necessary (at the time of writing this, it is not unlikely that there are some hidden ones missing).
1 parent f7be36b commit 175e0f1

File tree

10 files changed

+69
-32
lines changed

10 files changed

+69
-32
lines changed

doc/release/1.14.0-notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ New functions
2121
Deprecations
2222
============
2323

24+
* Multidimensional indexing with anything but a base class tuple is
25+
deprecated. This means that code such as ``arr[[slice(None)]]`` has to
26+
be changed to ``arr[tuple([slice(None)])]``. This is necessary to avoid
27+
the ambiguity of expressions such as ``arr[[[0, 1], [0, 1]]]`` which
28+
currently are interpreted as ``arr[[0, 1], [0, 1]]`` using heuristics.
29+
2430

2531
Future Changes
2632
==============

numpy/core/src/multiarray/mapping.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,15 @@ unpack_indices(PyObject *index, PyObject **result, npy_intp result_n)
313313
|| PySlice_Check(tmp_obj)
314314
|| tmp_obj == Py_Ellipsis
315315
|| tmp_obj == Py_None) {
316+
if (DEPRECATE_FUTUREWARNING(
317+
"Using a non-tuple sequence for multidimensional "
318+
"indexing is deprecated; use `arr[tuple(seq)]` "
319+
"instead of `arr[seq]`. In the future this will be "
320+
"interpreted as an array index, `arr[np.array(seq)]`, "
321+
"which will result either in an error or a different "
322+
"result.") < 0) {
323+
return -1;
324+
}
316325
commit_to_unpack = 1;
317326
}
318327
}

numpy/core/tests/test_deprecations.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,22 @@ class _VisibleDeprecationTestCase(_DeprecationTestCase):
132132
warning_cls = np.VisibleDeprecationWarning
133133

134134

135+
class TestNonTupleNDIndexDeprecation(object):
136+
def test_basic(self):
137+
a = np.zeros((5, 5))
138+
with warnings.catch_warnings():
139+
warnings.filterwarnings('always')
140+
assert_warns(FutureWarning, a.__getitem__, [[0, 1], [0, 1]])
141+
assert_warns(FutureWarning, a.__getitem__, [slice(None)])
142+
143+
warnings.filterwarnings('error')
144+
assert_raises(FutureWarning, a.__getitem__, [[0, 1], [0, 1]])
145+
assert_raises(FutureWarning, a.__getitem__, [slice(None)])
146+
147+
# a a[[0, 1]] always was advanced indexing, so no error/warning
148+
a[[0, 1]]
149+
150+
135151
class TestRankDeprecation(_DeprecationTestCase):
136152
"""Test that np.rank is deprecated. The function should simply be
137153
removed. The VisibleDeprecationWarning may become unnecessary.

numpy/core/tests/test_indexing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -513,7 +513,7 @@ def test_indexing_array_negative_strides(self):
513513
arro = np.zeros((4, 4))
514514
arr = arro[::-1, ::-1]
515515

516-
slices = [slice(None), [0, 1, 2, 3]]
516+
slices = (slice(None), [0, 1, 2, 3])
517517
arr[slices] = 10
518518
assert_array_equal(arr, 10.)
519519

numpy/core/tests/test_memmap.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,7 @@ def test_arithmetic_drops_references(self):
126126
def test_indexing_drops_references(self):
127127
fp = memmap(self.tmpfp, dtype=self.dtype, mode='w+',
128128
shape=self.shape)
129-
tmp = fp[[(1, 2), (2, 3)]]
129+
tmp = fp[((1, 2), (2, 3))]
130130
if isinstance(tmp, memmap):
131131
assert_(tmp._mmap is not fp._mmap)
132132

numpy/fft/fftpack.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,13 +69,13 @@ def _raw_fft(a, n=None, axis=-1, init_function=fftpack.cffti,
6969
if s[axis] > n:
7070
index = [slice(None)]*len(s)
7171
index[axis] = slice(0, n)
72-
a = a[index]
72+
a = a[tuple(index)]
7373
else:
7474
index = [slice(None)]*len(s)
7575
index[axis] = slice(0, s[axis])
7676
s[axis] = n
7777
z = zeros(s, a.dtype.char)
78-
z[index] = a
78+
z[tuple(index)] = a
7979
a = z
8080

8181
if axis != -1:

numpy/lib/arraypad.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1346,9 +1346,9 @@ def pad(array, pad_width, mode, **kwargs):
13461346
# Create a new padded array
13471347
rank = list(range(narray.ndim))
13481348
total_dim_increase = [np.sum(pad_width[i]) for i in rank]
1349-
offset_slices = [slice(pad_width[i][0],
1350-
pad_width[i][0] + narray.shape[i])
1351-
for i in rank]
1349+
offset_slices = tuple(
1350+
slice(pad_width[i][0], pad_width[i][0] + narray.shape[i])
1351+
for i in rank)
13521352
new_shape = np.array(narray.shape) + total_dim_increase
13531353
newmat = np.zeros(new_shape, narray.dtype)
13541354

numpy/lib/function_base.py

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1004,7 +1004,7 @@ def histogramdd(sample, bins=10, range=None, normed=False, weights=None):
10041004
ni[i], ni[j] = ni[j], ni[i]
10051005

10061006
# Remove outliers (indices 0 and -1 for each dimension).
1007-
core = D*[slice(1, -1)]
1007+
core = D * (slice(1, -1),)
10081008
hist = hist[core]
10091009

10101010
# Normalize if normed is True
@@ -1745,7 +1745,7 @@ def gradient(f, *varargs, **kwargs):
17451745
slice4[axis] = slice(2, None)
17461746

17471747
if uniform_spacing:
1748-
out[slice1] = (f[slice4] - f[slice2]) / (2. * dx[i])
1748+
out[tuple(slice1)] = (f[tuple(slice4)] - f[tuple(slice2)]) / (2. * dx[i])
17491749
else:
17501750
dx1 = dx[i][0:-1]
17511751
dx2 = dx[i][1:]
@@ -1757,7 +1757,7 @@ def gradient(f, *varargs, **kwargs):
17571757
shape[axis] = -1
17581758
a.shape = b.shape = c.shape = shape
17591759
# 1D equivalent -- out[1:-1] = a * f[:-2] + b * f[1:-1] + c * f[2:]
1760-
out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4]
1760+
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
17611761

17621762
# Numerical differentiation: 1st order edges
17631763
if edge_order == 1:
@@ -1766,14 +1766,14 @@ def gradient(f, *varargs, **kwargs):
17661766
slice3[axis] = 0
17671767
dx_0 = dx[i] if uniform_spacing else dx[i][0]
17681768
# 1D equivalent -- out[0] = (f[1] - f[0]) / (x[1] - x[0])
1769-
out[slice1] = (f[slice2] - f[slice3]) / dx_0
1769+
out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_0
17701770

17711771
slice1[axis] = -1
17721772
slice2[axis] = -1
17731773
slice3[axis] = -2
17741774
dx_n = dx[i] if uniform_spacing else dx[i][-1]
17751775
# 1D equivalent -- out[-1] = (f[-1] - f[-2]) / (x[-1] - x[-2])
1776-
out[slice1] = (f[slice2] - f[slice3]) / dx_n
1776+
out[tuple(slice1)] = (f[tuple(slice2)] - f[tuple(slice3)]) / dx_n
17771777

17781778
# Numerical differentiation: 2nd order edges
17791779
else:
@@ -1792,7 +1792,7 @@ def gradient(f, *varargs, **kwargs):
17921792
b = (dx1 + dx2) / (dx1 * dx2)
17931793
c = - dx1 / (dx2 * (dx1 + dx2))
17941794
# 1D equivalent -- out[0] = a * f[0] + b * f[1] + c * f[2]
1795-
out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4]
1795+
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
17961796

17971797
slice1[axis] = -1
17981798
slice2[axis] = -3
@@ -1809,7 +1809,7 @@ def gradient(f, *varargs, **kwargs):
18091809
b = - (dx2 + dx1) / (dx1 * dx2)
18101810
c = (2. * dx2 + dx1) / (dx2 * (dx1 + dx2))
18111811
# 1D equivalent -- out[-1] = a * f[-3] + b * f[-2] + c * f[-1]
1812-
out[slice1] = a * f[slice2] + b * f[slice3] + c * f[slice4]
1812+
out[tuple(slice1)] = a * f[tuple(slice2)] + b * f[tuple(slice3)] + c * f[tuple(slice4)]
18131813

18141814
outvals.append(out)
18151815

@@ -2159,6 +2159,7 @@ def unwrap(p, discont=pi, axis=-1):
21592159
dd = diff(p, axis=axis)
21602160
slice1 = [slice(None, None)]*nd # full slices
21612161
slice1[axis] = slice(1, None)
2162+
slice1 = tuple(slice1)
21622163
ddmod = mod(dd + pi, 2*pi) - pi
21632164
_nx.copyto(ddmod, pi, where=(ddmod == -pi) & (dd > 0))
21642165
ph_correct = ddmod - dd
@@ -4141,6 +4142,7 @@ def _median(a, axis=None, out=None, overwrite_input=False):
41414142
indexer[axis] = slice(index, index+1)
41424143
else:
41434144
indexer[axis] = slice(index-1, index+1)
4145+
indexer = tuple(indexer)
41444146

41454147
# Check if the array contains any nan's
41464148
if np.issubdtype(a.dtype, np.inexact) and sz > 0:
@@ -4497,12 +4499,12 @@ def trapz(y, x=None, dx=1.0, axis=-1):
44974499
slice1[axis] = slice(1, None)
44984500
slice2[axis] = slice(None, -1)
44994501
try:
4500-
ret = (d * (y[slice1] + y[slice2]) / 2.0).sum(axis)
4502+
ret = (d * (y[tuple(slice1)] + y[tuple(slice2)]) / 2.0).sum(axis)
45014503
except ValueError:
45024504
# Operations didn't work, cast to ndarray
45034505
d = np.asarray(d)
45044506
y = np.asarray(y)
4505-
ret = add.reduce(d * (y[slice1]+y[slice2])/2.0, axis)
4507+
ret = add.reduce(d * (y[tuple(slice1)]+y[tuple(slice2)])/2.0, axis)
45064508
return ret
45074509

45084510

@@ -4791,15 +4793,15 @@ def delete(arr, obj, axis=None):
47914793
pass
47924794
else:
47934795
slobj[axis] = slice(None, start)
4794-
new[slobj] = arr[slobj]
4796+
new[tuple(slobj)] = arr[tuple(slobj)]
47954797
# copy end chunck
47964798
if stop == N:
47974799
pass
47984800
else:
47994801
slobj[axis] = slice(stop-numtodel, None)
48004802
slobj2 = [slice(None)]*ndim
48014803
slobj2[axis] = slice(stop, None)
4802-
new[slobj] = arr[slobj2]
4804+
new[tuple(slobj)] = arr[tuple(slobj2)]
48034805
# copy middle pieces
48044806
if step == 1:
48054807
pass
@@ -4809,9 +4811,9 @@ def delete(arr, obj, axis=None):
48094811
slobj[axis] = slice(start, stop-numtodel)
48104812
slobj2 = [slice(None)]*ndim
48114813
slobj2[axis] = slice(start, stop)
4812-
arr = arr[slobj2]
4814+
arr = arr[tuple(slobj2)]
48134815
slobj2[axis] = keep
4814-
new[slobj] = arr[slobj2]
4816+
new[tuple(slobj)] = arr[tuple(slobj2)]
48154817
if wrap:
48164818
return wrap(new)
48174819
else:
@@ -4838,11 +4840,11 @@ def delete(arr, obj, axis=None):
48384840
newshape[axis] -= 1
48394841
new = empty(newshape, arr.dtype, arrorder)
48404842
slobj[axis] = slice(None, obj)
4841-
new[slobj] = arr[slobj]
4843+
new[tuple(slobj)] = arr[tuple(slobj)]
48424844
slobj[axis] = slice(obj, None)
48434845
slobj2 = [slice(None)]*ndim
48444846
slobj2[axis] = slice(obj+1, None)
4845-
new[slobj] = arr[slobj2]
4847+
new[tuple(slobj)] = arr[tuple(slobj2)]
48464848
else:
48474849
if obj.size == 0 and not isinstance(_obj, np.ndarray):
48484850
obj = obj.astype(intp)
@@ -4874,7 +4876,7 @@ def delete(arr, obj, axis=None):
48744876

48754877
keep[obj, ] = False
48764878
slobj[axis] = keep
4877-
new = arr[slobj]
4879+
new = arr[tuple(slobj)]
48784880

48794881
if wrap:
48804882
return wrap(new)
@@ -5045,13 +5047,13 @@ def insert(arr, obj, values, axis=None):
50455047
newshape[axis] += numnew
50465048
new = empty(newshape, arr.dtype, arrorder)
50475049
slobj[axis] = slice(None, index)
5048-
new[slobj] = arr[slobj]
5050+
new[tuple(slobj)] = arr[tuple(slobj)]
50495051
slobj[axis] = slice(index, index+numnew)
5050-
new[slobj] = values
5052+
new[tuple(slobj)] = values
50515053
slobj[axis] = slice(index+numnew, None)
50525054
slobj2 = [slice(None)] * ndim
50535055
slobj2[axis] = slice(index, None)
5054-
new[slobj] = arr[slobj2]
5056+
new[tuple(slobj)] = arr[tuple(slobj2)]
50555057
if wrap:
50565058
return wrap(new)
50575059
return new
@@ -5080,8 +5082,8 @@ def insert(arr, obj, values, axis=None):
50805082
slobj2 = [slice(None)]*ndim
50815083
slobj[axis] = indices
50825084
slobj2[axis] = old_mask
5083-
new[slobj] = values
5084-
new[slobj2] = arr
5085+
new[tuple(slobj)] = values
5086+
new[tuple(slobj2)] = arr
50855087

50865088
if wrap:
50875089
return wrap(new)

numpy/ma/core.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5483,6 +5483,7 @@ def sort(self, axis=-1, kind='quicksort', order=None,
54835483
else:
54845484
idx = list(np.ix_(*[np.arange(x) for x in self.shape]))
54855485
idx[axis] = sidx
5486+
idx = tuple(idx)
54865487

54875488
self[...] = self[idx]
54885489

@@ -6569,6 +6570,7 @@ def sort(a, axis=-1, kind='quicksort', order=None, endwith=True, fill_value=None
65696570
else:
65706571
a.sort(axis=axis, kind=kind, order=order)
65716572
return a
6573+
65726574
sort.__doc__ = MaskedArray.sort.__doc__
65736575

65746576

numpy/ma/extras.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ def _median(a, axis=None, out=None, overwrite_input=False):
723723
# as median (which is mean of empty slice = nan)
724724
indexer = [slice(None)] * asorted.ndim
725725
indexer[axis] = slice(0, 0)
726+
indexer = tuple(indexer)
726727
return np.ma.mean(asorted[indexer], axis=axis, out=out)
727728

728729
if asorted.ndim == 1:
@@ -784,6 +785,7 @@ def replace_masked(s):
784785
if np.issubdtype(asorted.dtype, np.inexact):
785786
# avoid inf / x = masked
786787
s = np.ma.sum([low, high], axis=0, out=out)
788+
print(repr(s.data))
787789
np.true_divide(s.data, 2., casting='unsafe', out=s.data)
788790

789791
s = np.lib.utils._median_nancheck(asorted, s, axis, out)
@@ -1657,7 +1659,7 @@ def flatnotmasked_contiguous(a):
16571659
if not k:
16581660
result.append(slice(i, i + n))
16591661
i += n
1660-
return result or None
1662+
return tuple(result) or None
16611663

16621664
def notmasked_contiguous(a, axis=None):
16631665
"""
@@ -1715,8 +1717,8 @@ def notmasked_contiguous(a, axis=None):
17151717
#
17161718
for i in range(a.shape[other]):
17171719
idx[other] = i
1718-
result.append(flatnotmasked_contiguous(a[idx]) or None)
1719-
return result
1720+
result.append(flatnotmasked_contiguous(a[tuple(idx)]) or None)
1721+
return tuple(result)
17201722

17211723

17221724
def _ezclump(mask):
@@ -1745,7 +1747,7 @@ def _ezclump(mask):
17451747

17461748
if mask[-1]:
17471749
r.append(slice(idx[-1], mask.size))
1748-
return r
1750+
return tuple(r)
17491751

17501752

17511753
def clump_unmasked(a):

0 commit comments

Comments
 (0)
0