在Python中计算列表的秩向量的高效方法

49

我希望能够在Python中高效地计算列表的排名向量,类似于R语言的rank函数。在没有元素之间存在平局的简单列表中,列表l的排名向量的第i个元素应为x,当且仅当l[i]是排序后的列表中第x个元素。到目前为止,这很简单,以下代码片段可以完成任务:

def rank_simple(vector):
    return sorted(range(len(vector)), key=vector.__getitem__)

然而,如果原始列表存在并列的元素(即多个具有相同值的元素),情况会变得复杂。在这种情况下,所有具有相同值的元素应该具有相同的排名,这是使用上述朴素方法获得的排名的平均值。例如,如果我有[1, 2, 3, 3, 3, 4, 5],那么朴素排名会给我[0, 1, 2, 3, 4, 5, 6],但我想要的是[0, 1, 3, 3, 3, 5, 6]。哪种方式在Python中实现最有效?


注:我不知道NumPy是否已经有一种方法可以实现这一点,如果有,请告诉我,但无论如何,我都希望得到一个纯Python的解决方案,因为我正在开发一个不需要NumPy也能工作的工具。


1
你有检查过 numpy.argsort(vector) 吗? - yosemite_k
顺便说一句,我认为这段代码甚至无法计算序数排名。要正确计算序数排名,请阅读此链接:https://codereview.stackexchange.com/questions/65031/creating-a-list-containing-the-rank-of-the-elements-in-the-original-list - H. Jang
几乎是 Rank items in an array using Python/NumPy, without sorting array twice - Stack Overflow 的副本,只不过另一个问题明确要求使用numpy解决方案。 - user202729
抱歉打扰了十一年,但是...你的rank_simple()实际上是R语言中order()函数的等价物,而不是rank()函数吗?例如,请参见https://dev59.com/02ct5IYBdhLWcg3wLqfw。 - djvg
13个回答

0

这些代码给了我很多灵感,特别是unutbu的代码。 然而我的需求更简单,所以我稍微改了一下代码。

希望能够帮助有相同需求的人们。

这里是用于记录玩家得分和排名的类。

class Player():
    def __init__(self, s, r):
        self.score = s
        self.rank = r

一些数据。

l = [Player(90,0),Player(95,0),Player(85,0), Player(90,0),Player(95,0)]

这是计算的代码:

l.sort(key=lambda x:x.score, reverse=True)    
l[0].rank = 1
dupcount = 0
prev = l[0]
for e in l[1:]:
    if e.score == prev.score:
        e.rank = prev.rank
        dupcount += 1
    else:
        e.rank = prev.rank + dupcount + 1
        dupcount = 0
        prev = e

0

排名函数可以使用以下方法在O(n log n)时间和O(n)额外空间内实现。

import bisect

def rank_list(lst: list[int]) -> list[int]:
    sorted_vals = sorted(set(lst))
    return [bisect.bisect_left(sorted_vals, val) for val in lst]

我在这里使用bisect库,但对于纯独立的代码来说,在已排序且具有唯一值的数组上实现二分查找过程就足够了,以查询现有(在此数组中)的值。


0

这适用于斯皮尔曼相关系数。

def get_rank(X, n):
    x_rank = dict((x, i+1) for i, x in enumerate(sorted(set(X))))
    return [x_rank[x] for x in X]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接