使用Python / NumPy排列数组中的项目

我有一个数组的数组,我想创build另一个数组,代表第一个数组中的每个项目的排名。 我正在使用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()] 

有没有更好/更快的方法避免sorting两次?

最后一步在左侧使用切片:

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

使用argsort两次,先获取数组的顺序,然后获取排名:

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

处理2D(或更高维)数组时,请确保将轴parameter passing给argsort以便在正确的轴上sorting。

这个问题已经有几年了,接受的答案很好,但是我认为以下几点还是值得一提的。 如果你不介意对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参数提供了几个处理关系的选项。 例如,在b有三次出现20次和两次出现40次:

 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.]) 

请参阅文档string以获取更多选项。

我试图扩展超过一维的数组A的解决scheme,假设你逐行处理数组(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的build议变成:

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

我随机生成400个形状(1000,100)的arrays; 第一个代码约7.5,第二个3.8。

对于平均排名的vector化版本,请参见下文。 我喜欢np.unique,它真的扩大了代码可以和不能有效vector化的范围。 除了避免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 

使用argsort()两次将做到这一点:

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

我尝试了上面的方法,但是失败了,因为我有很多zeores。 是的,即使是花车,重复的项目也许是重要的。

所以我写了一个修改的一维解决scheme,通过添加一个检查步骤:

 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)) 

我相信它是一样高效的。

我喜欢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. ]) 

我希望这也可以帮助别人,我试图find另一种解决scheme,但找不到任何…