我正在寻找一种有效的方法来计算Python中列表的等级向量,类似于R的rank
函数。在元素之间没有联系的简单列表中,列表l
的等级向量的元素 i 应该是 x 当且仅当{{1} }是排序列表中的 x -th元素。到目前为止,这很简单,以下代码片段可以解决这个问题:
l[i]
然而,如果原始列表具有联系(即具有相同值的多个元素),则事情变得复杂。在这种情况下,具有相同值的所有元素应该具有相同的等级,这是使用上述朴素方法获得的等级的平均值。所以,例如,如果我有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]
。在Python中哪一个是最有效的方法?
脚注:我不知道NumPy是否已经有了实现这一目标的方法;如果确实如此,请告诉我,但无论如何我都会对纯Python解决方案感兴趣,因为我正在开发一个无NumPy的工具。
答案 0 :(得分:55)
使用scipy,你正在寻找的功能是scipy.stats.rankdata:
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
的{{1}}函数的工作方式。
这是一个纯粹的python等价的rank
的rankdata函数:
scipy
答案 1 :(得分:4)
这是我为计算排名所写的功能之一。
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]
答案 2 :(得分:3)
这不会给出您指定的确切结果,但也许它会有用。以下代码段为每个元素提供第一个索引,产生最终的等级向量[0, 1, 2, 2, 2, 5, 6]
def rank_index(vector):
return [vector.index(x) for x in sorted(range(n), key=vector.__getitem__)]
您自己的测试必须证明这一点的效率。
答案 3 :(得分:2)
有一个名为Ranking http://pythonhosted.org/ranking/的非常好的模块,其中包含一个易于遵循的说明页面。要下载,只需使用easy_install ranking
答案 4 :(得分:2)
以下是unutbu代码的一小部分变体,包括绑定排名值的可选“方法”参数。
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
答案 5 :(得分:2)
[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]
答案 6 :(得分:1)
DECLARE
@StartDate DATE ='20160101' ,
@EndDate DATE = '20160331',
@Box1 DECIMAL,
@Box2 DECIMAL,
@Box3 DECIMAL,
@Box4 DECIMAL,
@Box5 DECIMAL,
@Box6 DECIMAL,
@Box7 DECIMAL,
@Box8 DECIMAL,
@Box9 DECIMAL
SET @Box1 = (SELECT ROUND(SUM (vt.Vat),2) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box2 = (SELECT ROUND(SUM(vt.VatDueOnECPurchases/vt.ConversionFactor),2) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box3 = (SELECT ROUND(SUM(@Box1 + @Box2),2))
SET @Box4 = (SELECT (ROUND(SUM(vt.VatInput),2) + @Box2) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box5 =(SELECT @Box3 - @Box4)
SET @Box8 = (SELECT ROUND(SUM(vt.SlAway/vt.ConversionFactor),2) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box9 = (SELECT ROUND(SUM(vt.PlAway/vt.ConversionFactor),2) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box6 = (SELECT (ROUND(SUM(vt.SlHome),2) + @Box8) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SET @Box7 = (SELECT (ROUND(SUM(vt.PlHome),2) + @Box9) FROM VatTransactions vt WHERE vt.VatTransactionDate BETWEEN @StartDate AND @EndDate)
SELECT @Box1 AS BOX1, @Box2 AS Box2, @Box3 AS Box3, @Box4 AS Box4, @Box5 AS Box5, @Box6 AS Box6, @Box7 AS Box7, @Box8 AS Box8, @Box9 AS Box9
timecomplexity是46.2us
答案 7 :(得分: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
答案 8 :(得分:0)
所以..这是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,这比到目前为止建议的其他方法要好:
.index()
的建议都很糟糕,运行时复杂度为N ^ 2; .index()
搜索时略有改进,但是在每次迭代中进行搜索和插入操作时,这在时间(NlogN)和空间方面仍然非常低效,并且还需要使用值可以散列。答案 9 :(得分:0)
我真的不明白为什么所有现有解决方案都如此复杂。可以这样做:
[index for element, index in sorted(zip(sequence, range(len(sequence))))]
您将构建包含元素和运行索引的元组。然后,您对整个事物进行排序,元组按其第一个元素排序,并且在联系期间按其第二个元素排序。这样,就可以对这些元组进行排序,然后只需要从中选择索引即可。同样,这也消除了以后需要按顺序查找元素的麻烦,这很可能使其成为O(N²)操作,而这是O(N log(N))。
答案 10 :(得分:0)
这适用于spearman相关系数。
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]
答案 11 :(得分: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 库,但对于纯独立代码,它足以在排序数组上实现二进制搜索过程,其中具有唯一值,用于查询现有(在此数组中)值。