在Python中计算列表的排名向量的高效方法
我在找一种高效的方法来计算Python中列表的排名向量,类似于R语言的rank
函数。在一个简单的列表中,如果元素之间没有相同的值,那么排名向量中第i个元素应该是x,当且仅当l[i]
是排序后列表中的第x个元素。到这里为止,这个逻辑很简单,下面的代码片段就能做到这一点:
def rank_simple(vector):
return sorted(range(len(vector)), key=vector.__getitem__)
不过,如果原始列表中有相同的值(也就是多个元素的值一样),事情就变得复杂了。在这种情况下,所有相同值的元素应该有相同的排名,而这个排名是通过上面提到的简单方法得到的排名的平均值。举个例子,如果我有[1, 2, 3, 3, 3, 4, 5]
,用简单的方法排名会得到[0, 1, 2, 3, 4, 5, 6]
,但我想要的结果是[0, 1, 3, 3, 3, 5, 6]
。在Python中,最有效的方法是什么呢?
附注:我不知道NumPy是否已经有实现这个功能的方法;如果有的话,请告诉我,但我还是希望能找到一个纯Python的解决方案,因为我正在开发一个不依赖NumPy的工具。
13 个回答
7
这是我写的一个函数,用来计算排名。
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]
输入:
calculate_rank([1,3,4,8,7,5,4,6])
输出:
[1, 2, 3, 7, 6, 4, 3, 5]
27
[sorted(l).index(x) for x in l]
sorted(l)
会返回一个排好序的列表。
index(x)
会告诉你在这个排好序的列表中,x
的位置。
举个例子:
l = [-1, 3, 2, 0,0]
>>> [sorted(l).index(x) for x in l]
[0, 4, 3, 1, 1]
87
在使用scipy库时,你需要找的函数是 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.])
这个函数的排名是从1开始的,而不是从0开始(就像你例子中的那样),不过这也是R
语言的rank
函数的工作方式。
下面是一个纯Python版本的scipy
的 rankdata 函数:
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]