我想得到离散数组的值,例如:
scores = [ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ]
这意味着( value, rank )
,例如3-1=2
500
,6-3=3
450
,所以真正的列表是:
[ 500, 500, 450, 450, 450, 400, 300, 300, 225]
现在,给定一个排名列表,我想知道这些排名的价值是什么,例如[1, 5, 7, 8]
,1st
值是500,5th
值是450, 7th
值为300,8th
值为300,因此返回[500, 450, 300, 300]
我该如何有效地做到这一点?
编辑:
让我们说len(scores)
是M,期望的列表长度是N,M>> N和M
可能非常大(无论如何这是3GB数据的静态结果)。
我的错误:
这是必要的:应该给出max rank
score
,以便知道最后一个的数量。
另一件事应该是重要的:分数没有分类,所以可能是:
scores = [(450, 3), (300, 7), (500, 1), (400, 6), (255, 9)]
total = 10 # this means we get two 255 here
答案 0 :(得分:3)
如果我们可以假设scores
中的排名按排序顺序出现,那么我们可以
使用bisect.bisect_right
在O(log(n))
时间内找到所需的索引。
如果len(scores)
很大,这比循环scores
更快
对于ranks
中的每个值:
import bisect
import operator
scores = [ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ]
values, cutoffs = zip(*scores)
ranks = [1, 5, 7, 8]
print([values[bisect.bisect_right(cutoffs, rank)-1] for rank in ranks])
产量
[500, 450, 300, 300]
对于非常大的len(scores)
或len(ranks)
,您可以使用NumPy获得更好的效果。相当于bisect.bisect
的NumPy为np.searchsorted
。但请注意,np.searchsorted
可以将ranks
的整个列表(或数组)作为参数并返回索引数组,而bisect.bisect
必须为每个rank
调用一次}。
因此,只需拨打一次np.searchsorted
来解决问题:
import numpy as np
scores = np.array([ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ])
values, cutoffs = scores[:,0], scores[:,1]
ranks = [1, 5, 7, 8]
print(values[np.searchsorted(cutoffs, ranks, side='right')-1])
# [500, 450, 300, 300]
以下是len(scores)
中等大时的基准测试。鉴于此设置:
import bisect
import numpy as np
scores = [ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ]
scores = np.array(scores * 2000).cumsum(axis=0)
values, cutoffs = scores[:,0], scores[:,1]
ranks = cutoffs[::100]
scores_list = list(map(tuple, scores))
ranks_list = list(ranks)
def using_numpy(scores, ranks):
# ranks does not have to be in sorted order
scores = np.asarray(scores)
values, cutoffs = scores[:,0], scores[:,1]
return values[np.searchsorted(cutoffs, ranks, side='right')-1]
def using_bisect(scores, ranks):
# ranks does not have to be in sorted order
values, cutoffs = zip(*scores)
return [values[bisect.bisect_right(cutoffs, rank)-1] for rank in ranks]
def using_reverse(scores, ranks):
# ranks must be sorted for this method to work
ranks.sort()
values = []
i = len(scores) - 1
for rank in reversed(ranks):
while scores[i][1] > rank:
i -= 1
values.append(scores[i][0])
values = values[::-1]
return values
这会检查结果是否相同:
for func in (using_reverse, using_bisect):
assert np.allclose(func(scores_list, ranks_list), using_numpy(scores, ranks))
如果您将NumPy数组传递给using_numpy
并列出到using_reverse
,则显示using_numpy
远远快于using_reverse
:
In [241]: %timeit using_numpy(scores, ranks)
100000 loops, best of 3: 8.58 µs per loop
In [239]: %timeit using_reverse(scores_list, ranks_list)
1000 loops, best of 3: 827 µs per loop
In [250]: %timeit using_bisect(scores_list, ranks_list)
1000 loops, best of 3: 835 µs per loop
如果包含将列表scores_list
转换为NumPy所需的时间
数组,然后using_numpy
变得慢于using_reverse
:
In [242]: %timeit using_numpy(scores_list, ranks_list)
100 loops, best of 3: 3.77 ms per loop
但是,通常当您使用NumPy时,您构建数组一次,然后 然后执行许多计算。因此,转换为NumPy数组时 昂贵的,它是一次性成本,比如说<4ms(对于上面的例子)。后 哪,你可以享受快速的基于NumPy的计算(在上面的例子中, 几乎快了100倍)。如果你的程序很短,那么在一段时间内完成 大约4ms,然后转换为NumPy数组可能是 太昂贵了。但是,如果您的程序需要更长的时间 超过4毫秒(这通常是性能重要的情况),那么转换为NumPy数组将是一个很小的代价。
答案 1 :(得分:1)
这是一种方式。我主要通过在队伍中行走并将索引标记为适当的分数来并行处理分数和排名。我倒退因为那更容易。如果你有很多分数,只有很少的等级可以查找,那么二分搜索可能会更好。
values = []
i = len(scores) - 1
for rank in reversed(ranks):
while scores[i][1] > rank:
i -= 1
values.append(scores[i][0])
values = values[::-1]
或者,如果你的意思是&#34;有效&#34; ,就像简短代码一样,这里有两种方法:
>>> [min(v for v, r in scores if r <= rank) for rank in ranks]
[500, 450, 300, 300]
>>> [next(v for v, r in scores[::-1] if r <= rank) for rank in ranks]
[500, 450, 300, 300]
更新我刚刚看到了@unutbu的基准测试,因为他忽略了最快解决方案的准备成本,并为其他解决方案提供了不合适的数据类型,并忽略了我的主要解决方案(只是基准测试我的慢那些??)并使用了原始的ranks
而没有使它适应他完全不同的scores
,我自己做了一个更公平的比较:
(更新2: unutbu更新了他的基准并解决了所有这些问题)
same result: True
7.620430773809753 seconds for numpy
4.5830411517407095 seconds for parallel
使用unutbu&#39; scores
但改为原始数据类型(元组列表)和改编后的ranks
。它的10000分和100个等级像原版一样分散(我也尝试了10个等级和1000个等级,时间并没有太大变化)。代码:
from timeit import timeit
import numpy as np
def search_numpy(scores, ranks):
scores = np.array(scores)
values, cutoffs = scores[:,0], scores[:,1]
return values[np.searchsorted(cutoffs, ranks, side='right')-1]
def search_parallel(scores, ranks):
values = []
i = len(scores) - 1
for rank in reversed(ranks):
while scores[i][1] > rank:
i -= 1
values.append(scores[i][0])
return values[::-1]
scores = np.array([ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ] * 2000).cumsum(axis=0)
values, cutoffs = scores[:,0], scores[:,1]
ranks = cutoffs[::100]
scores = list(map(tuple, scores))
ranks = list(ranks)
print('same result:', all(search_parallel(scores, ranks) == search_numpy(scores, ranks)))
print(timeit(lambda: search_numpy(scores, ranks), number=1000), 'seconds for numpy')
print(timeit(lambda: search_parallel(scores, ranks), number=1000), 'seconds for parallel')
答案 2 :(得分:0)
rank = [1, 5, 7, 8]
scores = [ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ]
arr = []
for i in xrange(1, len(scores)):
arr.extend([scores[i-1][0]] * abs(scores[i][1] - scores[i-1][1]))
arr.append(scores[-1][0])
print arr
for i in rank:
print arr[i-1],
>>> [500, 500, 450, 450, 450, 400, 300, 300, 225]
>>> 500 450 300 300
答案 3 :(得分:0)
创建了两个函数:
功能1 :从输入中创建从1到最后一个等级的所有等级的值。
Fucntion2 :从目标排名列表和值列表中创建目标值列表
<强>演示强>:
def getScores(scores):
""" Create Value rank list from 1 to last rank"""
start_rank = 1
start_value = 0
last_rank = scores[-1][1]
value_scores = []
for value, rank in scores:
if start_rank==rank:
value_scores.append(value)
start_rank += 1
elif start_rank<rank:
# Set Previous Value to rank if not present in the Input.
for j in range(start_rank, rank+1):
value_scores.append(value)
start_rank += 1
return value_scores
def getRankScores(target_rank, value_scores):
""" Get Target Rank Value"""
target_values = []
for i in target_rank:
try:
target_values.append(value_scores[i-1])
except IndexError:
#- Outof Range.
#Handle exception for Rank which value is not present in the Values.
target_values.append(value_scores[-1])
return target_values
scores = [ (500, 1), (450, 3), (400, 6), (300, 7), (225, 9) ]
target_rank = [1, 5, 7, 8]
value_scores = getScores(scores)
print "value_scores:", value_scores
target_values = getRankScores(target_rank, value_scores)
print "Result:-", target_values
<强>输出强>:
value_scores: [500, 450, 450, 400, 400, 400, 300, 225, 225]
Result:- [500, 400, 300, 225]
答案 4 :(得分:0)
我尝试使用itertools
。
import itertools
scores = [(500, 1), (450, 3), (400, 6), (300, 7), (225, 9)]
result = [[scores[i][0]for j in range(abs(scores[i+1][1]-scores[i][1]))] if i+1 != len(scores) else [scores[-1][0]]
for i in range(len(scores))]
score_list = list(itertools.chain(*result))
print score_list
结果:
[500, 500, 450, 450, 450, 400, 300, 300, 225]
现在基于score_list
,你可以根据排名获得价值。
rank_list = [1, 5, 7, 8]
for i in rank_list:
print "Score holding rank {} :- {}".format(i, score_list[i-1])
显示器:
Score holding rank 1 :- 500
Score holding rank 5 :- 450
Score holding rank 7 :- 300
Score holding rank 8 :- 300
答案 5 :(得分:0)
如果我理解正确,你没有在scores
中映射每个等级 - 值关系,但是有规则:如果你没有找到分数中的等级,那么选择下一个较小的等级。
在以下函数中,我找到小于或等于给定等级的最大等级(和相应的值)。然后我只使用这个值。
def find_rank_value(to_be_looked_up_rank):
proxy_rank, value = max((rank, value) for value, rank in scores if rank<= to_be_looked_up_rank)
return value
现在我们可以使用此函数使用列表推导来生成值列表。
list_of_ranks = [1, 5, 7, 8]
list_of_values = [find_rank_value(rank) for rank in list_of_ranks]
list_of_values
输出:
[500, 450, 300, 300]
答案 6 :(得分:0)
scores = [(500, 1), (450, 3), (400, 6), (300, 7), (225, 9)]
ranks = [1, 5, 7, 8]
values = []
v, r = zip(*scores)
for rank in ranks:
while rank not in r:
rank -= 1
values.append(v[r.index(rank)])