2010-06-18 87 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]