Scipy rankdata 反向从高到低排名

10 投票
4 回答
10544 浏览
提问于 2025-04-18 10:00

我想要实现的是把一组数值从高到低进行排名,简单来说,就是想要一个和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 个回答

0

这里有至少两种简单的方法可以做到这一点,但在过程中有一些细节需要注意,我会一一指出。

把数据转换成整数并不是个好主意,除非原始的排名本来就是整数,这种情况下你根本不需要转换!在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)
0

要注意这两个答案都有一个问题,当你使用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)没有像原始数据那样被翻转。

10

另一种方法是把列表中的数字变成负数:

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

我觉得这样做更准确,因为这样处理相同的数字时,是根据倒过来的排名来决定的,而不是根据正常的排名。此外,在这种情况下,最小的值会被赋予列表最后一个位置的值,这样的结果也是大家通常所期待的。

14

这可能是个你不想听的简单回答,但你难道不能直接用长度相减的方法,也就是从高到低的顺序来“反转”吗?

a = [1,2,3,4,3,2,3,4]
len(a) - rankdata(a).astype(int)
array([7, 6, 3, 1, 3, 6, 3, 1])

撰写回答