user11335389
user11335389

Reputation:

How to rank a list of numbers and print out the ranking?

I have a list of numbers that I'm trying to rank, starting from 1 (highest) then print that out. For numbers with ties, I'd just like to number them in order, whichever comes first.

lst = [[0.0] , [0.0] , [0.0] , [0.1] , [0.2]]

#expected output
rank_lst = [3,4,5,2,1]

I just want a simple function similar to what I've done:

rank_lst = [sorted(lst).index(values) for values in lst]

However this one starts at 0, ranks the lowest number as 0 and ranks tied values with the same number like below:

#output
rank_list = [0,0,0,1,2]

Upvotes: 7

Views: 3148

Answers (4)

3UqU57GnaX
3UqU57GnaX

Reputation: 399

You can use numpy.argsort()

Since you want sorted from high to low, you can first make the values negative.

import numpy as np

lst = [[0.0] , [0.0] , [0.0] , [0.1] , [0.2]]
np_lst = np.array([v[0] for v in lst])
rank_lst = list(np.argsort(np.argsort(-np_lst)) + 1) 

Edit: and a second np.argsort() to get what you want.

Upvotes: 4

RoadRunner
RoadRunner

Reputation: 26335

You can try using a collections.defaultdict to collect the indices of the list in reverse then just pop the indices off the left one at time with collections.deque.

from collections import defaultdict
from collections import deque

lst = [0, 1, 0, 2, 0]

d = defaultdict(deque)
for i, x in enumerate(sorted(lst, reverse=True), start=1):
    d[x].append(i)

result = [d[x].popleft() for x in lst]

print(result)
# [3, 2, 4, 1, 5]

Or with nested lists like in the question:

from collections import defaultdict
from collections import deque
from operator import itemgetter

lst = [[0.0], [0.0], [0.0], [0.1], [0.2]]

single = list(map(itemgetter(0), lst))

d = defaultdict(deque)
for i, x in enumerate(sorted(single, reverse=True), start=1):
    d[x].append(i)

result = [d[x].popleft() for x in single]

print(result)
# [3, 4, 5, 2, 1]

Upvotes: 3

Matthias
Matthias

Reputation: 13232

You could do it like this

lst = [[0.0], [0.0], [0.0], [0.1], [0.2]]
# wanted: rank_lst == [3, 4, 5, 2, 1]

# add original position
data = list(enumerate(lst, start=1))
# -> [(1, [0.0]), (2, [0.0]), (3, [0.0]), (4, [0.1]), (5, [0.2])]

# sort by value
data = list(sorted(data, key=lambda x: x[1], reverse=True))
# -> [(5, [0.2]), (4, [0.1]), (1, [0.0]), (2, [0.0]), (3, [0.0])]

# add sorted position
data = list(enumerate(data, start=1))
# -> [(1, (5, [0.2])), (2, (4, [0.1])), (3, (1, [0.0])), (4, (2, [0.0])), (5, (3, [0.0]))]

# resort to original order
data = list(sorted(data, key=lambda x: x[1][0]))
# -> [(3, (1, [0.0])), (4, (2, [0.0])), (5, (3, [0.0])), (2, (4, [0.1])), (1, (5, [0.2]))]

# extract sorted order number
rank_lst = [x[0] for x in data]
# -> [3, 4, 5, 2, 1]

Of course you can throw out of all those calls to list. I only used those to create the output.

Upvotes: 3

cammil
cammil

Reputation: 9937

>>> lst = [[0.0] , [0.0] , [0.0] , [0.1] , [0.2]]
>>> lst1 = [i[0] for i in lst] # you only need numbers, not lists for each value
>>> xx = list(enumerate(lst1)) # add an "index" to each value
>>> xx.sort(key=lambda i: -i[1]) # sort on values
>>> xxx = [(i[0], i[1], j+1) for j, i in enumerate(xx)] # add the rank of each value
>>> xxx.sort(key=lambda i : i[0]) # put them back in the original order using the index
>>> res = [i[2] for i in xxx] # get just the ranks
>>> res
[3, 4, 5, 2, 1]

Upvotes: -1

Related Questions