2

I have a list:

 somelist = [500, 600, 200, 1000]

I want to generate the rank order of that list:

 rankorderofsomelist = [3, 2, 4, 1]

There are some complex solutions, but does anyone have any simple methods?

Asclepius
  • 57,944
  • 17
  • 167
  • 143
Chris J. Vargo
  • 2,266
  • 7
  • 28
  • 43
  • Pretty much a duplicate of [Rank items in an array using Python/NumPy, without sorting array twice - Stack Overflow](https://stackoverflow.com/questions/5284646/rank-items-in-an-array-using-python-numpy-without-sorting-array-twice) -- that question explicitly asks for numpy solution, however. – user202729 Jan 11 '21 at 10:47

3 Answers3

7

Simplest I can think of:

rankorder = sorted(range(len(thelist)), key=thelist.__getitem__)

This will, of course, produce [2, 1, 3, 0], because Python indexing is always zero-based -- if for some absolutely weird reason you need to add one to each index you can of course easily do so:

rankorder_weird = [1+x for x in rankorder]
Alex Martelli
  • 854,459
  • 170
  • 1,222
  • 1,395
6

Since you've tagged this question scipy, you could use scipy.stats.rankdata:

>>> rankdata(somelist)
array([ 2.,  3.,  1.,  4.])
>>> len(somelist) - rankdata(somelist)
array([ 2.,  1.,  3.,  0.])
>>> len(somelist) - rankdata(somelist) + 1
array([ 3.,  2.,  4.,  1.])

The real advantage is that you can specify how you want the corner cases to be treated:

>>> rankdata([0,1,1,2])
array([ 1. ,  2.5,  2.5,  4. ])
>>> rankdata([0,1,1,2], method='min')
array([ 1,  2,  2,  4])
>>> rankdata([0,1,1,2], method='dense')
array([ 1,  2,  2,  3])
Asclepius
  • 57,944
  • 17
  • 167
  • 143
DSM
  • 342,061
  • 65
  • 592
  • 494
4

Try this one-liner:

rankorderofsomelist = [sorted(somelist).index(x) for x in somelist]

Note that it'll behave as expected for a list with multiple entries of the same value (e.g. four instances of the same value, all of them the second-largest in the list, will all be ranked 2). Also note that Pythonic sorting is ascending (smallest to largest) and zero-based, so you may have to apply a final pass over the list to increment the ranks, reverse them, etc.

You can include that pass in the one-liner. To yield your desired result, just use:

rankorderofsomelist = [len(somelist)-(sorted(somelist).index(x)) for x in somelist]
Newb
  • 2,810
  • 3
  • 21
  • 35
  • this works well. It doesn't handle ties as well as the scipy method, but I didn't specify that in the question. – Chris J. Vargo Mar 02 '15 at 00:55
  • 2
    This has, if I am not mistaken, N^3log(N) complexity: for every item in the list you are sorting the list, then searching it linearly for that particular item. Even if you sorted the list before, instead of in the loop, you would still be looking at N^2 complexity. You [could do worse](http://c2.com/cgi/wiki?SlowSort), but you would have to try really hard. It is a terrible suggestion. -1, I'm afraid... – Jaime Mar 02 '15 at 01:22
  • 1
    @Jaime OP asked for a simple solution, so I showed him one. Besides, it's **trivial** to reduce the time-complexity: just store `sorted(somelist)` in a variable above the one-liner. That yields a worst-case complexity of O(n^2). – Newb Mar 02 '15 at 04:43