2010-06-18 89 views
22

我正在寻找一种有效的方法来计算Python中列表的列向量,类似于R的rank函数。在一个简单的列表与所述元件之间没有联系,元件列表l的秩向量的应X当且仅当是l[i]在排序列表中的X个元件。这是简单的,到目前为止,下面的代码片段的伎俩:有效的方法来计算Python列表中的列表向量

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的工具。

+0

你检查过'numpy.argsort(vector)'吗? – 2016-10-03 07:55:19

回答

40

使用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(如在你的例子),但话又说回来,这就是方法Rrank函数也适用。

这里是一个纯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] 
3

这不给你指定的确切结果,但也许这将是有益的反正。下面的代码片段给人的第一指标的每个元素,产生[0, 1, 2, 2, 2, 5, 6]

def rank_index(vector): 
    return [vector.index(x) for x in sorted(range(n), key=vector.__getitem__)] 

自己的测试的最终排名矢量必须证明这样做的效率。

+0

这假设'vector'已经排序,但仍然是一个非常容易理解的实现。 +1 – tgray 2010-06-18 17:26:58

+0

啊,好点。 Tamás的理解从一个排序的()列表开始......我将编辑以包含它。 – 2010-06-18 17:42:45

+2

不仅假设不成立,而且index()方法也是O(N),所以根本没有效率。 – zinking 2014-07-16 10:40:03

2

下面是unutbu代码的一个小变体,包括绑定行的值类型的可选“方法”参数。

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 
+0

谢谢!最近版本的scipy.stats.rankdata有可选的方法参数,但我坚持使用只支持平均方法的旧版本,所以你为我节省了很多时间编写自己的函数。如果你添加一个“密集”选项,那么你将会覆盖这一切。 – kslnet 2015-09-23 16:06:22

0

这些代码给了我很多灵感,特别是unutbu的代码。但是我的需求比较简单,所以我稍微改了一些代码。

希望能帮助有相同需求的人。

这里是记录玩家的得分和排名的课程。

class Player(): 
    def __init__(self, s, r): 
     self.score = s 
     self.rank = r 

有些数据。

l = [Player(90,0),Player(95,0),Player(85,0), Player(90,0),Player(95,0)] 

这里是计算的代码:

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 
1
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是46.2us

1

这是我写来计算排名的功能之一。

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]