在Python中计算列表的排名向量的高效方法

49 投票
13 回答
104758 浏览
提问于 2025-04-16 00:09

我在找一种高效的方法来计算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版本的scipyrankdata 函数:

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]

撰写回答