2011-03-12 16 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