Skip to content

Commit ca096a3

Browse files
authored
Merge pull request numpy#26914 from seberg/partial-revert-unique-inverse
API: Partially revert unique with return_inverse
2 parents 244509c + 6160c58 commit ca096a3

File tree

3 files changed

+25
-15
lines changed

3 files changed

+25
-15
lines changed

doc/source/release/2.0.0-notes.rst

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,6 +1496,12 @@ the ``unique_inverse`` output is now shaped such that the input can be reconstru
14961496
directly using ``np.take(unique, unique_inverse)`` when ``axis=None``, and
14971497
``np.take_along_axis(unique, unique_inverse, axis=axis)`` otherwise.
14981498

1499+
.. note::
1500+
This change was reverted in 2.0.1 except for ``axis=None``. The correct
1501+
reconstruction is always ``np.take(unique, unique_inverse, axis=axis)``.
1502+
When 2.0.0 needs to be supported, add ``unique_inverse.reshape(-1)``
1503+
to code.
1504+
14991505
(`gh-25553 <https://github.com/numpy/numpy/pull/25553>`__,
15001506
`gh-25570 <https://github.com/numpy/numpy/pull/25570>`__)
15011507

numpy/lib/_arraysetops_impl.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -228,8 +228,13 @@ def unique(ar, return_index=False, return_inverse=False,
228228
.. versionchanged: 2.0
229229
For multi-dimensional inputs, ``unique_inverse`` is reshaped
230230
such that the input can be reconstructed using
231-
``np.take(unique, unique_inverse)`` when ``axis = None``, and
232-
``np.take_along_axis(unique, unique_inverse, axis=axis)`` otherwise.
231+
``np.take(unique, unique_inverse, axis=axis)``. The result is
232+
now not 1-dimensional when ``axis=None``.
233+
234+
Note that in NumPy 2.0.0 a higher dimensional array was returned also
235+
when ``axis`` was not ``None``. This was reverted, but
236+
``inverse.reshape(-1)`` can be used to ensure compatibility with both
237+
versions.
233238
234239
Examples
235240
--------
@@ -282,7 +287,7 @@ def unique(ar, return_index=False, return_inverse=False,
282287
ar = np.asanyarray(ar)
283288
if axis is None:
284289
ret = _unique1d(ar, return_index, return_inverse, return_counts,
285-
equal_nan=equal_nan, inverse_shape=ar.shape)
290+
equal_nan=equal_nan, inverse_shape=ar.shape, axis=None)
286291
return _unpack_tuple(ret)
287292

288293
# axis was specified and not None
@@ -328,13 +333,15 @@ def reshape_uniq(uniq):
328333

329334
output = _unique1d(consolidated, return_index,
330335
return_inverse, return_counts,
331-
equal_nan=equal_nan, inverse_shape=inverse_shape)
336+
equal_nan=equal_nan, inverse_shape=inverse_shape,
337+
axis=axis)
332338
output = (reshape_uniq(output[0]),) + output[1:]
333339
return _unpack_tuple(output)
334340

335341

336342
def _unique1d(ar, return_index=False, return_inverse=False,
337-
return_counts=False, *, equal_nan=True, inverse_shape=None):
343+
return_counts=False, *, equal_nan=True, inverse_shape=None,
344+
axis=None):
338345
"""
339346
Find the unique elements of an array, ignoring shape.
340347
"""
@@ -371,7 +378,7 @@ def _unique1d(ar, return_index=False, return_inverse=False,
371378
imask = np.cumsum(mask) - 1
372379
inv_idx = np.empty(mask.shape, dtype=np.intp)
373380
inv_idx[perm] = imask
374-
ret += (inv_idx.reshape(inverse_shape),)
381+
ret += (inv_idx.reshape(inverse_shape) if axis is None else inv_idx,)
375382
if return_counts:
376383
idx = np.concatenate(np.nonzero(mask) + ([mask.size],))
377384
ret += (np.diff(idx),)

numpy/lib/tests/test_arraysetops.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -828,11 +828,8 @@ def test_unique_1d_with_axis(self, axis):
828828
def test_unique_inverse_with_axis(self, axis):
829829
x = np.array([[4, 4, 3], [2, 2, 1], [2, 2, 1], [4, 4, 3]])
830830
uniq, inv = unique(x, return_inverse=True, axis=axis)
831-
assert_equal(inv.ndim, x.ndim)
832-
if axis is None:
833-
assert_array_equal(x, np.take(uniq, inv))
834-
else:
835-
assert_array_equal(x, np.take_along_axis(uniq, inv, axis=axis))
831+
assert_equal(inv.ndim, x.ndim if axis is None else 1)
832+
assert_array_equal(x, np.take(uniq, inv, axis=axis))
836833

837834
def test_unique_axis_zeros(self):
838835
# issue 15559
@@ -844,7 +841,7 @@ def test_unique_axis_zeros(self):
844841
assert_equal(uniq.dtype, single_zero.dtype)
845842
assert_array_equal(uniq, np.empty(shape=(1, 0)))
846843
assert_array_equal(idx, np.array([0]))
847-
assert_array_equal(inv, np.array([[0], [0]]))
844+
assert_array_equal(inv, np.array([0, 0]))
848845
assert_array_equal(cnt, np.array([2]))
849846

850847
# there's 0 elements of shape (2,) along axis 1
@@ -854,7 +851,7 @@ def test_unique_axis_zeros(self):
854851
assert_equal(uniq.dtype, single_zero.dtype)
855852
assert_array_equal(uniq, np.empty(shape=(2, 0)))
856853
assert_array_equal(idx, np.array([]))
857-
assert_array_equal(inv, np.empty((1, 0)))
854+
assert_array_equal(inv, np.array([]))
858855
assert_array_equal(cnt, np.array([]))
859856

860857
# test a "complicated" shape
@@ -923,7 +920,7 @@ def _run_axis_tests(self, dtype):
923920
msg = "Unique's return_index=True failed with axis=0"
924921
assert_array_equal(data[idx], uniq, msg)
925922
msg = "Unique's return_inverse=True failed with axis=0"
926-
assert_array_equal(np.take_along_axis(uniq, inv, axis=0), data)
923+
assert_array_equal(np.take(uniq, inv, axis=0), data)
927924
msg = "Unique's return_counts=True failed with axis=0"
928925
assert_array_equal(cnt, np.array([2, 2]), msg)
929926

@@ -932,7 +929,7 @@ def _run_axis_tests(self, dtype):
932929
msg = "Unique's return_index=True failed with axis=1"
933930
assert_array_equal(data[:, idx], uniq)
934931
msg = "Unique's return_inverse=True failed with axis=1"
935-
assert_array_equal(np.take_along_axis(uniq, inv, axis=1), data)
932+
assert_array_equal(np.take(uniq, inv, axis=1), data)
936933
msg = "Unique's return_counts=True failed with axis=1"
937934
assert_array_equal(cnt, np.array([2, 1, 1]), msg)
938935

0 commit comments

Comments
 (0)