Reputation: 14082
I have a list:
[55, 41, 45, 43, 60, 47, 33, 70, 42, 42, 44]
I would like to create a new list which ranks these items in the order they are:
Expected Output:
[7, ,2 ,9 ,10, 4, 11, 3, 6, 1, 5, 8]
Tried these 3 version of the func but its not working properly, not sure why?
def argsort(seq):
#return sorted(range(len(seq)), key = seq.__getitem__)
#return [i for (v, i) in sorted((v, i) for (i, v) in enumerate(seq))]
return [x for x,y in sorted(enumerate(seq), key = lambda x: x[1])]
returns:
[6, 1, 8, 9, 3, 10, 2, 5, 0, 4, 7]
Upvotes: 6
Views: 10286
Reputation: 113975
Let's come up with a more precise definition of what you are after:
Given a list L of numbers, create a list M that contains the valid indices of L, such that
M[i]
is the index at which thei
th element of L occurs, when sorted. PresentM
as if the first element of L occurs at index 1.
Now, there are two ways of the valid indices of a list:
range(len(L))
: gives a sequence of numbers 0... len(L)-1enumerate(L)
: gives a sequence of tuples (0, L[0])... (len(L)-1, L[len(L)-1])
Since you want to operate under the assumption that list indices start at 1 (when in fact they start at 0 in python), we need to do a little off-setting
range(len(L))
becomes `range(1, len(L)+1)enumerate(L)
becomes enumerate(L, 1)
(the optional argument tells enumerate
to start numbering at 1)Since we need the list items as well, let's use enumerate
, rather than use range
and do a lookup (I personally prefer this method, though both are valid)
So now, we can get a list of tuples, in which each tuple contains the index of an element in L and the corresponding element in L. We need to sort this by the elements themselves. This can be done with a call to sorted
, with the optional key
argument explaining that we should sort by the second element of the tuple (operator.itemgetter(1)
says exactly this).
Once we have a sorted list of such tuples, all we need to do is extract the first element of each tuple (the index in L), we can use the list-comprehension [i[0] for i in mylist]
, or equivalently [operator.itemgetter(0)(t) for t in mylist]
Putting all these elements together, we get the following
In [138]: L = [55, 41, 45, 43, 60, 47, 33, 70, 42, 42, 44]
In [139]: [operator.itemgetter(0)(t) for t in sorted(enumerate(L,1), key=operator.itemgetter(1))]
Out[139]: [7, 2, 9, 10, 4, 11, 3, 6, 1, 5, 8]
Upvotes: 8