Je recherche un moyen efficace de calculer le vecteur de classement d'une liste en Python, similaire à la fonction rank
de R. Dans une liste simple sans liens entre les éléments, l'élément i du vecteur de rang d'une liste l
doit être x si et seulement si l[i]
est le x - ème élément de l'élément trié liste. C'est simple jusqu'à présent, l'extrait de code suivant fait l'affaire:
def rank_simple(vector):
return sorted(range(len(vector)), key=vector.__getitem__)
Les choses se compliquent toutefois si la liste d'origine est liée (c'est-à-dire plusieurs éléments de même valeur). Dans ce cas, tous les éléments ayant la même valeur doivent avoir le même rang, qui est la moyenne de leurs rangs obtenue selon la méthode naïve ci-dessus. Ainsi, par exemple, si j'ai [1, 2, 3, 3, 3, 4, 5]
, le classement naïf me donne [0, 1, 2, 3, 4, 5, 6]
, mais ce que j'aimerais avoir, c'est [0, 1, 3, 3, 3, 5, 6]
. Lequel serait le moyen le plus efficace de faire cela en Python?
Note de bas de page: Je ne sais pas si NumPy a déjà une méthode pour y parvenir ou non; Si c'est le cas, merci de me le faire savoir, mais je serais de toute façon intéressé par une solution Python pure, car je développe un outil qui devrait également fonctionner sans NumPy.
En utilisant scipy, la fonction que vous recherchez est scipy.stats.rankdata:
In [13]: import scipy.stats as ss
In [19]: ss.rankdata([3, 1, 4, 15, 92])
Out[19]: array([ 2., 1., 3., 4., 5.])
In [20]: ss.rankdata([1, 2, 3, 3, 3, 4, 5])
Out[20]: array([ 1., 2., 4., 4., 4., 6., 7.])
Les rangs commencent à 1 au lieu de 0 (comme dans votre exemple), mais là encore, c'est la façon dont la fonction R
de rank
fonctionne également.
Voici un équivalent pur en python de la fonction rankdata de scipy
:
def rank_simple(vector):
return sorted(range(len(vector)), key=vector.__getitem__)
def rankdata(a):
n = len(a)
ivec=rank_simple(a)
svec=[a[rank] for rank in ivec]
sumranks = 0
dupcount = 0
newarray = [0]*n
for i in xrange(n):
sumranks += i
dupcount += 1
if i==n-1 or svec[i] != svec[i+1]:
averank = sumranks / float(dupcount) + 1
for j in xrange(i-dupcount+1,i+1):
newarray[ivec[j]] = averank
sumranks = 0
dupcount = 0
return newarray
print(rankdata([3, 1, 4, 15, 92]))
# [2.0, 1.0, 3.0, 4.0, 5.0]
print(rankdata([1, 2, 3, 3, 3, 4, 5]))
# [1.0, 2.0, 4.0, 4.0, 4.0, 6.0, 7.0]
Cela ne donne pas le résultat exact que vous spécifiez, mais peut-être serait-il utile de toute façon. L'extrait suivant donne le premier index pour chaque élément, donnant un dernier vecteur de rang de [0, 1, 2, 2, 2, 5, 6]
def rank_index(vector):
return [vector.index(x) for x in sorted(range(n), key=vector.__getitem__)]
Vos propres tests devraient prouver l'efficacité de ceci.
C'est l'une des fonctions que j'ai écrites pour calculer le rang.
def calculate_rank(vector):
a={}
rank=1
for num in sorted(vector):
if num not in a:
a[num]=rank
rank=rank+1
return[a[i] for i in vector]
contribution:
calculate_rank([1,3,4,8,7,5,4,6])
sortie:
[1, 2, 3, 7, 6, 4, 3, 5]
Il existe un module vraiment sympa appelé Ranking http://pythonhosted.org/ranking/ avec une page d’instructions facile à suivre. Pour télécharger, utilisez simplement easy_install ranking
Voici une petite variation du code de unutbu, incluant un argument optionnel 'méthode' pour le type de valeur des rangs liés.
def rank_simple(vector):
return sorted(range(len(vector)), key=vector.__getitem__)
def rankdata(a, method='average'):
n = len(a)
ivec=rank_simple(a)
svec=[a[rank] for rank in ivec]
sumranks = 0
dupcount = 0
newarray = [0]*n
for i in xrange(n):
sumranks += i
dupcount += 1
if i==n-1 or svec[i] != svec[i+1]:
for j in xrange(i-dupcount+1,i+1):
if method=='average':
averank = sumranks / float(dupcount) + 1
newarray[ivec[j]] = averank
Elif method=='max':
newarray[ivec[j]] = i+1
Elif method=='min':
newarray[ivec[j]] = i+1 -dupcount+1
else:
raise NameError('Unsupported method')
sumranks = 0
dupcount = 0
return newarray
import numpy as np
def rankVec(arg):
p = np.unique(arg) #take unique value
k = (-p).argsort().argsort() #sort based on arguments in ascending order
dd = defaultdict(int)
for i in xrange(np.shape(p)[0]):
dd[p[i]] = k[i]
return np.array([dd[x] for x in arg])
timecomplexity est 46.2us
[sorted(l).index(x) for x in l]
sorted(l)
donnera la version triée index(x)
donnera la index
dans le tableau trié
par exemple :
l = [-1, 3, 2, 0,0]
>>> [sorted(l).index(x) for x in l]
[0, 4, 3, 1, 1]
Ces codes me donnent beaucoup d’inspiration, en particulier le code de unutbu. Cependant, mes besoins sont plus simples, j’ai donc légèrement modifié le code.
Espérant aider les gars ayant les mêmes besoins.
Voici la classe pour enregistrer les scores et les classements des joueurs.
class Player():
def __init__(self, s, r):
self.score = s
self.rank = r
Quelques données.
l = [Player(90,0),Player(95,0),Player(85,0), Player(90,0),Player(95,0)]
Voici le code pour le calcul:
l.sort(key=lambda x:x.score, reverse=True)
l[0].rank = 1
dupcount = 0
prev = l[0]
for e in l[1:]:
if e.score == prev.score:
e.rank = prev.rank
dupcount += 1
else:
e.rank = prev.rank + dupcount + 1
dupcount = 0
prev = e