代码之家  ›  专栏  ›  技术社区  ›  Tamás

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

  •  24
  • Tamás  · 技术社区  · 15 年前

    rank 功能。在元素之间没有关系的简单列表中,元素 l 应该是 当且仅当 l[i] -排序列表中的第个元素。到目前为止,这很简单,下面的代码片段实现了这一点:

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

    但是,如果原始列表有关联(即具有相同值的多个元素),事情就会变得复杂。在这种情况下,具有相同值的所有元素都应该具有相同的秩,这是使用上面的naive方法获得的它们的秩的平均值。例如,如果我有 [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的工具。

    8 回复  |  直到 14 年前
        1
  •  72
  •   unutbu    14 年前

    In [13]: import scipy.stats as ss
    In [19]: ss.rankdata([3, 1, 4, 15, 92])
    Out[19]: array([ 2.,  1.,  3.,  4.,  5.])
    
    In [20]: ss.rankdata([1, 2, 3, 3, 3, 4, 5])
    Out[20]: array([ 1.,  2.,  4.,  4.,  4.,  6.,  7.])
    

    列组从1开始,而不是0(如您的示例中所示),但同样,这就是方法 R rank 函数也起作用。

    scipy

    def rank_simple(vector):
        return sorted(range(len(vector)), key=vector.__getitem__)
    
    def rankdata(a):
        n = len(a)
        ivec=rank_simple(a)
        svec=[a[rank] for rank in ivec]
        sumranks = 0
        dupcount = 0
        newarray = [0]*n
        for i in xrange(n):
            sumranks += i
            dupcount += 1
            if i==n-1 or svec[i] != svec[i+1]:
                averank = sumranks / float(dupcount) + 1
                for j in xrange(i-dupcount+1,i+1):
                    newarray[ivec[j]] = averank
                sumranks = 0
                dupcount = 0
        return newarray
    
    print(rankdata([3, 1, 4, 15, 92]))
    # [2.0, 1.0, 3.0, 4.0, 5.0]
    print(rankdata([1, 2, 3, 3, 3, 4, 5]))
    # [1.0, 2.0, 4.0, 4.0, 4.0, 6.0, 7.0]
    
        2
  •  14
  •   Alex Taylor    6 年前
    [sorted(l).index(x) for x in l]
    

    sorted(l) index(x) 将给 index 在排序数组中

    l = [-1, 3, 2, 0,0]
    >>> [sorted(l).index(x) for x in l]
    [0, 4, 3, 1, 1]
    
        3
  •  6
  •   Banghua Zhao    6 年前

    这是我写的计算等级的函数之一。

    def calculate_rank(vector):
      a={}
      rank=1
      for num in sorted(vector):
        if num not in a:
          a[num]=rank
          rank=rank+1
      return[a[i] for i in vector]
    

    输入:

    calculate_rank([1,3,4,8,7,5,4,6])
    

    输出:

    [1, 2, 3, 7, 6, 4, 3, 5]
    
        4
  •  4
  •   stw_dev    15 年前

    [0, 1, 2, 2, 2, 5, 6]

    def rank_index(vector):
        return [vector.index(x) for x in sorted(range(n), key=vector.__getitem__)]
    

    你自己的测试必须证明这是有效的。

        5
  •  2
  •   Kerry Kalweit    11 年前

    有一个非常好的模块叫做排名 http://pythonhosted.org/ranking/ 有一个易于遵循的说明页。要下载,只需使用 easy_install ranking

        6
  •  2
  •   Sunthar    10 年前

    这里是unutbu代码的一个小变体,包括一个可选的'method'参数,用于绑定列组的值类型。

    def rank_simple(vector):
        return sorted(range(len(vector)), key=vector.__getitem__)
    
    def rankdata(a, method='average'):
        n = len(a)
        ivec=rank_simple(a)
        svec=[a[rank] for rank in ivec]
        sumranks = 0
        dupcount = 0
        newarray = [0]*n
        for i in xrange(n):
            sumranks += i
            dupcount += 1
            if i==n-1 or svec[i] != svec[i+1]:
                for j in xrange(i-dupcount+1,i+1):
                    if method=='average':
                        averank = sumranks / float(dupcount) + 1
                        newarray[ivec[j]] = averank
                    elif method=='max':
                        newarray[ivec[j]] = i+1
                    elif method=='min':
                        newarray[ivec[j]] = i+1 -dupcount+1
                    else:
                        raise NameError('Unsupported method')
    
                sumranks = 0
                dupcount = 0
    
    
        return newarray
    
        7
  •  2
  •   Martin Ueding    5 年前

    我真的不明白为什么所有现有的解决方案都这么复杂。可以这样做:

    [index for element, index in sorted(zip(sequence, range(len(sequence))))]
    

    您将构建包含元素和运行索引的元组。然后你对整件事进行排序,元组按它们的第一个元素排序,而ties则按它们的第二个元素排序。这样就有了这些元组的排序列表,之后只需要从中挑选索引。此外,这也消除了以后在序列中查找元素的需要,这很可能使其成为O(N)操作,而这是O(N log(N))。

        8
  •  0
  •   Joe    9 年前

    但是我的需求比较简单,所以我稍微修改了代码。

    这是记录球员得分和排名的课程。

    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
    
        9
  •  0
  •   vamsi21    8 年前
    import numpy as np
    
    def rankVec(arg):
        p = np.unique(arg) #take unique value
        k = (-p).argsort().argsort() #sort based on arguments in ascending order
        dd = defaultdict(int)
        for i in xrange(np.shape(p)[0]):
            dd[p[i]] = k[i]
        return np.array([dd[x] for x in arg])
    

    时间复杂度为46.2us

        10
  •  0
  •   Jonathan H    5 年前

    所以。。现在是2019年,我不知道为什么没有人提出以下建议:

    # Python-only
    def rank_list( x, break_ties=False ):
        n = len(x)
        t = list(range(n))
        s = sorted( t, key=x.__getitem__ )
    
        if not break_ties:
            for k in range(n-1):
                t[k+1] = t[k] + (x[s[k+1]] != x[s[k]])
    
        r = s.copy()
        for i,k in enumerate(s):
            r[k] = t[i]
    
        return r
    
    # Using Numpy, see also: np.argsort
    def rank_vec( x, break_ties=False ):
        n = len(x)
        t = np.arange(n)
        s = sorted( t, key=x.__getitem__ )
    
        if not break_ties:
            t[1:] = np.cumsum(x[s[1:]] != x[s[:-1]])
    
        r = t.copy()
        np.put( r, s, t )
        return r
    

    这种方法在初始排序之后具有线性运行时复杂性,它只存储2个索引数组,并且不要求值是可散列的(只需要成对比较)。

    AFAICT,这比目前建议的其他方法更好:

    • @联合国大学的方法基本上是相似的,但是(我认为)对于OP的要求来说太复杂了;
    • 所有建议使用 .index()
    • .index()
        11
  •  0
  •   behanm    4 年前

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

    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]