2011-03-12 17 views
49

我有一個數組數組,我想創建另一個數組,代表第一個數組中的每個項目的排名。我正在使用Python和NumPy。使用Python/NumPy排列數組中的項目

例如:

array = [4,2,7,1] 
ranks = [2,1,3,0] 

下面是我想出的最好方法:

array = numpy.array([4,2,7,1]) 
temp = array.argsort() 
ranks = numpy.arange(len(array))[temp.argsort()] 

是否有更好的/更快的方法是避免排序陣列的兩倍?在最後一步的左側

+5

您的最後一行等同於'ranks = temp.argsort()'。 – 2011-03-12 19:03:58

回答

33

使用切片:

array = numpy.array([4,2,7,1]) 
temp = array.argsort() 
ranks = numpy.empty_like(temp) 
ranks[temp] = numpy.arange(len(array)) 

這樣就避免了在最後一步顛倒排列排序的兩倍。

+1

完美,謝謝!我知道有一個解決方案,看到它就顯得很明顯。我用timeit做了一些測試,對於小數組,這種方法稍微慢一些。在我的機器上,當數組有2000個元素時,它們是相等的。在20,000個元素上,你的方法快了大約25%。 – joshayers 2011-03-12 20:33:17

+0

有關如何進行此行的任何建議? – Xaser 2017-11-19 14:09:08

62

使用argsort兩次,第一次,以獲得陣列的順序,然後獲得排名:

array = numpy.array([4,2,7,1]) 
order = array.argsort() 
ranks = order.argsort() 

當與2D(或更高維)陣列處理,一定要傳遞一個軸參數argsort到順序在正確的軸上。

+2

請注意,如果數字在您的輸入數組中重複(例如'[4,2,7,1,1]'),輸出將根據它們的數組位置('[3,2,4,0, 1]') – rcoup 2011-06-08 01:53:34

+0

很好的回答!非常pythonic。 – 2014-04-14 19:00:41

+4

排序兩次效率不高。 @Sven Marnach的回答顯示瞭如何通過一次調用'argsort'完成排名。 – 2015-07-13 15:13:12

4

我試圖擴展超過一個維度的數組A的解決方案,假設您逐行處理數組(axis = 1)。

我用循環在行上擴展了第一個代碼;大概可以提高

temp = A.argsort(axis=1) 
rank = np.empty_like(temp) 
rangeA = np.arange(temp.shape[1]) 
for iRow in xrange(temp.shape[0]): 
    rank[iRow, temp[iRow,:]] = rangeA 

,第二個,以下k.rooijers建議,變爲:

temp = A.argsort(axis=1) 
rank = temp.argsort(axis=1) 

我隨機生成400個陣列具有形狀(1000,100);第一個代碼大約7.5,第二個3.8。

2

我試過上面的方法,但因爲我有很多zeores而失敗。是的,即使有浮標,重複項目也可能很重要。

所以我加入了領帶檢查步驟寫了修改1D方案:

def ranks (v): 
    import numpy as np 
    t = np.argsort(v) 
    r = np.empty(len(v),int) 
    r[t] = np.arange(len(v)) 
    for i in xrange(1, len(r)): 
     if v[t[i]] <= v[t[i-1]]: r[t[i]] = r[t[i-1]] 
    return r 

# test it 
print sorted(zip(ranks(v), v)) 

我相信這是有效率,因爲它可以。

0

我喜歡k.rooijers的方法,但正如rcoup寫的,重複的數字按照數組的位置排列。這對我來說沒有好處,所以我修改的版本進行後處理隊伍,任何重複的數字合併成一個綜合平均等級:

import numpy as np 
a = np.array([4,2,7,2,1]) 
r = np.array(a.argsort().argsort(), dtype=float) 
f = a==a 
for i in xrange(len(a)): 
    if not f[i]: continue 
    s = a == a[i] 
    ls = np.sum(s) 
    if ls > 1: 
     tr = np.sum(r[s]) 
     r[s] = float(tr)/ls 
    f[s] = False 

print r # array([ 3. , 1.5, 4. , 1.5, 0. ]) 

我希望這可以幫助別人也一樣,我試圖找到anothers解決這個,但找不到任何...

3

對於平均排名的矢量化版本,請參見下文。我喜歡np.unique,它真的擴大了代碼可以或不可能有效矢量化的範圍。除了避免python for循環外,這種方法還可以避免隱含的'a'雙循環。

import numpy as np 

a = np.array([4,1,6,8,4,1,6]) 

a = np.array([4,2,7,2,1]) 
rank = a.argsort().argsort() 

unique, inverse = np.unique(a, return_inverse = True) 

unique_rank_sum = np.zeros_like(unique) 
np.add.at(unique_rank_sum, inverse, rank) 
unique_count = np.zeros_like(unique) 
np.add.at(unique_count, inverse, 1) 

unique_rank_mean = unique_rank_sum.astype(np.float)/unique_count 

rank_mean = unique_rank_mean[inverse] 

print rank_mean 
+0

;我讓這個代碼產生與其他平均等級代碼相同的輸出,但是我可以想象一組重複數的最小等級也是一樣的。 >>> unique,index,inverse = np.unique(a,True,True) >>> rank_min = rank [index] [inverse] – 2014-01-06 20:47:21

+0

我收到以下錯誤消息解決方案(numpy 1.7.1): AttributeError:'numpy。' – Fear 2017-07-14 09:33:18

+0

ufunc'對象沒有屬性'這需要更新版本的numpy;你的是很古老的 – 2017-07-14 10:08:38

3

使用argsort()兩次將做到這一點:

>>> array = [4,2,7,1] 
>>> ranks = numpy.array(array).argsort().argsort() 
>>> ranks 
array([2, 1, 3, 0]) 
+1

這是[已經提到](http://stackoverflow.com/a/6266510/786559)之前,你提出了你的答案 – 2017-04-09 16:26:45

46

這個問題是幾年的歷史,並接受的答案是偉大的,但我認爲下面還是值得一提。如果你不介意scipy的依賴,你可以使用scipy.stats.rankdata

In [22]: from scipy.stats import rankdata 

In [23]: a = [4, 2, 7, 1] 

In [24]: rankdata(a) 
Out[24]: array([ 3., 2., 4., 1.]) 

In [25]: (rankdata(a) - 1).astype(int) 
Out[25]: array([2, 1, 3, 0]) 

rankdata一個很好的功能是,method參數提供用於處理關係的幾個選項。例如,有三個事件20的和兩次出現的40 b

In [26]: b = [40, 20, 70, 10, 20, 50, 30, 40, 20] 

默認的平均等級分配給並列值:

In [27]: rankdata(b) 
Out[27]: array([ 6.5, 3. , 9. , 1. , 3. , 8. , 5. , 6.5, 3. ]) 

method='ordinal'分配連續的行列:

In [28]: rankdata(b, method='ordinal') 
Out[28]: array([ 6., 2., 9., 1., 3., 8., 5., 7., 4.]) 

method='min'將綁定值的最小等級分配給所有綁定值:

In [29]: rankdata(b, method='min') 
Out[29]: array([ 6., 2., 9., 1., 2., 8., 5., 6., 2.]) 

請參閱文檔字符串瞭解更多選項。

+1

你的答案確實非常合適。我不確定爲什麼你被拒絕給予完全合法的選擇。 – Dexter 2015-04-27 20:53:30

+1

是的,這是邊緣案例很重要的地方的最佳答案。 – naught101 2015-10-20 02:54:47