Reputation:
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
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
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
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
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