Scipy rankdata 反向从高到低排名
我想要实现的是把一组数值从高到低进行排名,简单来说,就是想要一个和rankdata相反的结果。
所以我不想要这样的:
a = [1,2,3,4,3,2,3,4]
rankdata(a).astype(int)
array([1, 2, 5, 7, 5, 2, 5, 7])
我想要得到的是这样的:
array([7, 6, 3, 1, 3, 6, 3, 1])
我在rankdata的文档里没有找到可以做到这一点的方法。
4 个回答
这里有至少两种简单的方法可以做到这一点,但在过程中有一些细节需要注意,我会一一指出。
把数据转换成整数并不是个好主意,除非原始的排名本来就是整数,这种情况下你根本不需要转换!在rankdata()
中处理并列排名的默认方法是"average"
,如果有并列的话,这样会导致排名是小数。这大概就是提问者想要转换成整数的原因。如果你使用其他方法,比如"max"
、"min"
或"ordinal"
,那么排名本身就已经是整数了。我假设提问者对此是可以接受的,所以接下来我会使用method="max"
。
先排名再反转(更快)
在这种方法中,我们先进行排名,然后再反转顺序。
len(a) - rankdata(a, method="max") + 1
这样得到的结果是:
[8 6 3 1 3 6 3 1]
这里的+ 1
是必须的,因为rankdata()
返回的最大排名是len(a)
,但我们希望最小的排名永远不为0。注意,尽管我们最开始是用"max"
来处理并列的,但由于反转的原因,我们得到了"min"
的效果。例如,两个2的出现实际上在排名6和7的位置上,最终都被分配了6,这是这两个排名中的最小值,而不是最大值。
先反转再排名(更慢)
这种方法需要遍历列表,把值乘以-1,这样会让速度变慢:
rankdata([-el for el in a], method="max")
这样得到的结果是:
array([8, 7, 5, 2, 5, 7, 5, 2])
在这种情况下,我们实际上得到了"max"
的效果。例如,两个2的出现实际上在排名6和7的位置上,最终都被分配了7,这是这两个排名中的最大值。这就是导致排名1没有出现的原因,因为我们在排名1和2之间有并列,最终得到了排名2,这是这两个排名中的最大值。
时间效率
如果你想在长输入上进行这个操作,并且速度是个问题,那么很明显第一种方法会更快。例如,如果我们使用以下输入:
a = numpy.random.randint(100, size=1000).tolist()
那么我们得到:
%timeit len(a) - rankdata(a, method="max") + 1
90.5 µs ± 3.43 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
%timeit rankdata([-el for el in a], method="max")
121 µs ± 1.65 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
要注意这两个答案都有一个问题,当你使用argsort的时候,会导致排序不一致,这种情况只会在有相同值的时候出现。
比如:
a=[1,1,2,3,4]
np.argsort(stats.rankdata(a))=array([0, 1, 2, 3, 4], dtype=int64
np.argsort(stats.rankdata([-1*i for i in a])) = array([4, 3, 2, 0, 1], dtype=int64)
np.argsort(5-stats.rankdata(a))= array([4, 3, 2, 0, 1], dtype=int64)
注意到反转会导致相同值的排序不一致(0和1)没有像原始数据那样被翻转。
另一种方法是把列表中的数字变成负数:
>>> from scipy.stats import rankdata
>>> a = [1,2,3,4,3,2,3,4]
>>> rankdata([-1 * i for i in a]).astype(int)
array([8, 6, 4, 1, 4, 6, 4, 1])
我觉得这样做更准确,因为这样处理相同的数字时,是根据倒过来的排名来决定的,而不是根据正常的排名。此外,在这种情况下,最小的值会被赋予列表最后一个位置的值,这样的结果也是大家通常所期待的。
这可能是个你不想听的简单回答,但你难道不能直接用长度相减的方法,也就是从高到低的顺序来“反转”吗?
a = [1,2,3,4,3,2,3,4]
len(a) - rankdata(a).astype(int)
array([7, 6, 3, 1, 3, 6, 3, 1])