Here's one approach:

Use numpy.unique to both sort the array and remove duplicate items. Pass the return_inverse argument to get the indices into the sorted array that give the values of the original array. Then, you can get all of the indices of the tied items by finding the indices of the inverse array whose values are equal to the index into the unique array for that item.

For example:

foo = array([3, 1, 4, 0, 1, 0])
foo_unique, foo_inverse = unique(foo, return_inverse=True)

# Put largest items first
foo_unique = foo_unique[::-1]
foo_inverse = -foo_inverse + len(foo_unique) - 1

foo_top3 = foo_unique[:3]

# Get the indices into foo of the top item
first_indices = (foo_inverse == 0).nonzero()

# Choose one at random
first_random_idx = random.choice(first_indices)

second_indices = (foo_inverse == 1).nonzero()
second_random_idx = random.choice(second_indices)

# And so on...

numpy.unique is implemented using argsort, so a glance at its implementation might suggest a simpler approach.

Answer from codewarrior on Stack Overflow
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.4 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
🌐
NumPy
numpy.org › devdocs › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.5.dev0 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.sort.html
numpy.sort — NumPy v2.4 Manual
When a is an array with fields defined, this argument specifies which fields to compare first, second, etc. A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties.
🌐
NumPy
numpy.org › doc › 2.2 › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.2 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
🌐
Finxter
blog.finxter.com › np-argsort
np.argsort() — A Simpe Illustrated Guide – Be on the Right Side of Change
July 3, 2022 - A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties.” · Intuitively, the order argument sets the sorting order for an input array with defined fields.
🌐
NumPy
numpy.org › doc › 2.1 › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.1 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
🌐
NumPy
numpy.org › doc › 2.3 › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.3 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
Find elsewhere
🌐
YouTube
youtube.com › luke chaffey
Can numpy argsort return lower index for ties? - YouTube
python: Can numpy argsort return lower index for ties?Thanks for taking the time to learn more. In this video I'll go through your question, provide various ...
Published   December 4, 2023
Views   1
🌐
NumPy
numpy.org › doc › stable › reference › generated › numpy.argpartition.html
numpy.argpartition — NumPy v2.4 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Array of indices that partition a along the specified axis. If a is one-dimensional, a[index_array] yields a partitioned a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the partitioned a, irrespective of dimensionality.
🌐
NumPy
numpy.org › doc › 1.25 › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v1.25 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality. ... Describes sorting algorithms used. ... Indirect stable sort with multiple keys. ... Inplace sort. ... Indirect partial sort. ... Apply index_array from argsort to an array as if by calling sort.
🌐
GitHub
github.com › jax-ml › jax › issues › 2124
JAX argsort has different behavior on ties compared to {np,tf}.argsort · Issue #2124 · jax-ml/jax
January 29, 2020 - Do you know if this behavior is also shared by TF argsort when XLA-compiled (and/or if the XLA OpKernel has to go through hoops to get there?) ... TF2XLA will sort -0 < 0, because it uses the following comparator: https://github.com/tensorflow/tensorflow/blob/97f3666d8c10d317a050d786c645d181cbb4d0bb/tensorflow/compiler/xla/client/lib/comparators.cc#L70 · We can easily do the same in JAX. Edit: that's actually what we are already doing already, if no comparator is specified. ... In [9]: np.argsort([-0., 0.]) Out[9]: array([0, 1]) In [10]: np.argsort([0., -0.]) Out[10]: array([0, 1]) In [15]: tf.argsort(np.array([0., -0.])) Out[15]: <tf.Tensor: id=32, shape=(2,), dtype=int32, numpy=array([0, 1], dtype=int32)> In [16]: tf.argsort(np.array([-0., 0.])) Out[16]: <tf.Tensor: id=43, shape=(2,), dtype=int32, numpy=array([0, 1], dtype=int32)>
Top answer
1 of 10
174

According to the documentation

Returns the indices that would sort an array.

  • 2 is the index of 0.0.
  • 3 is the index of 0.1.
  • 1 is the index of 1.41.
  • 0 is the index of 1.48.
2 of 10
51

[2, 3, 1, 0] indicates that the smallest element is at index 2, the next smallest at index 3, then index 1, then index 0.

There are a number of ways to get the result you are looking for:

import numpy as np
import scipy.stats as stats

def using_indexed_assignment(x):
    "https://stackoverflow.com/a/5284703/190597 (Sven Marnach)"
    result = np.empty(len(x), dtype=int)
    temp = x.argsort()
    result[temp] = np.arange(len(x))
    return result

def using_rankdata(x):
    return stats.rankdata(x)-1

def using_argsort_twice(x):
    "https://stackoverflow.com/a/6266510/190597 (k.rooijers)"
    return np.argsort(np.argsort(x))

def using_digitize(x):
    unique_vals, index = np.unique(x, return_inverse=True)
    return np.digitize(x, bins=unique_vals) - 1

For example,

In [72]: x = np.array([1.48,1.41,0.0,0.1])

In [73]: using_indexed_assignment(x)
Out[73]: array([3, 2, 0, 1])

This checks that they all produce the same result:

x = np.random.random(10**5)
expected = using_indexed_assignment(x)
for func in (using_argsort_twice, using_digitize, using_rankdata):
    assert np.allclose(expected, func(x))

These IPython %timeit benchmarks suggests for large arrays using_indexed_assignment is the fastest:

In [50]: x = np.random.random(10**5)
In [66]: %timeit using_indexed_assignment(x)
100 loops, best of 3: 9.32 ms per loop

In [70]: %timeit using_rankdata(x)
100 loops, best of 3: 10.6 ms per loop

In [56]: %timeit using_argsort_twice(x)
100 loops, best of 3: 16.2 ms per loop

In [59]: %timeit using_digitize(x)
10 loops, best of 3: 27 ms per loop

For small arrays, using_argsort_twice may be faster:

In [78]: x = np.random.random(10**2)

In [81]: %timeit using_argsort_twice(x)
100000 loops, best of 3: 3.45 µs per loop

In [79]: %timeit using_indexed_assignment(x)
100000 loops, best of 3: 4.78 µs per loop

In [80]: %timeit using_rankdata(x)
100000 loops, best of 3: 19 µs per loop

In [82]: %timeit using_digitize(x)
10000 loops, best of 3: 26.2 µs per loop

Note also that stats.rankdata gives you more control over how to handle elements of equal value.

🌐
YouTube
youtube.com › hey delphi
Array : Can numpy argsort handle ties? - YouTube
Array : Can numpy argsort handle ties?To Access My Live Chat Page, On Google, Search for "hows tech developer connect"I promised to reveal a secret feature t...
Published   April 20, 2023
Views   8
🌐
NumPy
numpy.org › doc › 2.0 › reference › generated › numpy.argsort.html
numpy.argsort — NumPy v2.0 Manual
A single field can be specified as a string, and not all fields need be specified, but unspecified fields will still be used, in the order in which they come up in the dtype, to break ties. ... Sort stability. If True, the returned array will maintain the relative order of a values which compare as equal. If False or None, this is not guaranteed. Internally, this option selects kind='stable'. Default: None. New in version 2.0.0. ... Array of indices that sort a along the specified axis. If a is one-dimensional, a[index_array] yields a sorted a. More generally, np.take_along_axis(a, index_array, axis=axis) always yields the sorted a, irrespective of dimensionality.
🌐
Statology
statology.org › home › how to rank items in numpy array (with examples)
How to Rank Items in NumPy Array (With Examples)
November 4, 2022 - By default, argsort() uses an ordinal method for handling ties which means the tied value that occurs first is automatically given the lower rank.