Find the unique elements of an array, ignoring shape. Uses a hash table to find the unique elements if possible.
(ar, return_index=False, return_inverse=False,
return_counts=False, *, equal_nan=True, inverse_shape=None,
axis=None, sorted=True)
| 348 | |
| 349 | |
| 350 | def _unique1d(ar, return_index=False, return_inverse=False, |
| 351 | return_counts=False, *, equal_nan=True, inverse_shape=None, |
| 352 | axis=None, sorted=True): |
| 353 | """ |
| 354 | Find the unique elements of an array, ignoring shape. |
| 355 | |
| 356 | Uses a hash table to find the unique elements if possible. |
| 357 | """ |
| 358 | ar = np.asanyarray(ar).flatten() |
| 359 | if len(ar.shape) != 1: |
| 360 | # np.matrix, and maybe some other array subclasses, insist on keeping |
| 361 | # two dimensions for all operations. Coerce to an ndarray in such cases. |
| 362 | ar = np.asarray(ar).flatten() |
| 363 | |
| 364 | optional_indices = return_index or return_inverse |
| 365 | |
| 366 | # masked arrays are not supported yet. |
| 367 | if not optional_indices and not return_counts and not np.ma.is_masked(ar): |
| 368 | # First we convert the array to a numpy array, later we wrap it back |
| 369 | # in case it was a subclass of numpy.ndarray. |
| 370 | conv = _array_converter(ar) |
| 371 | ar_, = conv |
| 372 | |
| 373 | if (hash_unique := _unique_hash(ar_, equal_nan=equal_nan)) \ |
| 374 | is not NotImplemented: |
| 375 | if sorted: |
| 376 | hash_unique.sort() |
| 377 | # We wrap the result back in case it was a subclass of numpy.ndarray. |
| 378 | return (conv.wrap(hash_unique),) |
| 379 | |
| 380 | # If we don't use the hash map, we use the slower sorting method. |
| 381 | if optional_indices: |
| 382 | perm = ar.argsort(kind='mergesort' if return_index else 'quicksort') |
| 383 | aux = ar[perm] |
| 384 | else: |
| 385 | ar.sort() |
| 386 | aux = ar |
| 387 | mask = np.empty(aux.shape, dtype=np.bool) |
| 388 | mask[:1] = True |
| 389 | if (equal_nan and aux.shape[0] > 0 and aux.dtype.kind in "cfmM" and |
| 390 | np.isnan(aux[-1])): |
| 391 | if aux.dtype.kind == "c": # for complex all NaNs are considered equivalent |
| 392 | aux_firstnan = np.searchsorted(np.isnan(aux), True, side='left') |
| 393 | else: |
| 394 | aux_firstnan = np.searchsorted(aux, aux[-1], side='left') |
| 395 | if aux_firstnan > 0: |
| 396 | mask[1:aux_firstnan] = ( |
| 397 | aux[1:aux_firstnan] != aux[:aux_firstnan - 1]) |
| 398 | mask[aux_firstnan] = True |
| 399 | mask[aux_firstnan + 1:] = False |
| 400 | else: |
| 401 | mask[1:] = aux[1:] != aux[:-1] |
| 402 | |
| 403 | ret = (aux[mask],) |
| 404 | if return_index: |
| 405 | ret += (perm[mask],) |
| 406 | if return_inverse: |
| 407 | imask = np.cumsum(mask) - 1 |