Python中的位置排名和处理平局
(我之前的问题版本显示了错误的函数,我为此道歉,现在已经修正了,希望这个问题现在更容易理解。)
我有一个包含分数的对象列表,我想根据这些分数给它们排个名。下面是我输出数据的基本方式。
sorted_scores = [
('Apolo Ohno', 0),
('Shanie Davis', -1),
('Bodie Miller', -2),
('Lindsay Vohn', -3),
('Shawn White', -3),
('Bryan Veloso', -4)
]
现在我遇到了平局。现在给这些对象分配位置的函数是一个简单的循环,它只是把循环的计数器 i
作为对象的最终位置。
positions = {}
i = 1
for key, value in sorted_list:
# Since in my codebase the strings are IDs, I use the key to fetch the object.
if value is not None:
positions[key] = i
i += 1
所以这显然会返回:
positions = {
'Apolo Ohno': 1,
'Shanie Davis': 2,
'Bodie Miller': 3,
'Lindsay Vohn': 4,
'Shawn White': 5,
'Bryan Veloso': 6
}
希望这能让你明白。问题的关键在于这个循环。更合理的做法是,如果它能这样返回结果:
positions = {
'Apolo Ohno': 1,
'Shanie Davis': 2,
'Bodie Miller': 3,
'Lindsay Vohn': 4, # Same value.
'Shawn White': 4, # Same value.
'Bryan Veloso': 6
}
我该如何修改上面的函数来实现这个功能呢?需要考虑的是,可能会有任意数量的平局,具体取决于有多少人给这个对象排名。最高的排名应该是1,这样可以显示为: <rank>/<total # of people>
提前谢谢你。:)
10 个回答
2
要做到这一点,不是去计算这个元素在某个随意的顺序中的位置,而是要计算有多少其他元素的得分更高。
编辑:
根据大家的要求,优化到O(n)了,所有内容都在这里:
positions = {}
cur_score = None # Score we're examining
cur_count = 0 # Number of others that we've seen with this score
for ix, (name, score) in enumerate(sorted_scores):
if score == cur_score: # Same score for this player as previous
cur_count += 1
else: # Different score from before
cur_score = score
cur_count = 0
positions[name] = ix - cur_count + 1 # Add 1 because ix is 0-based
print positions
3
=== 更新:在更改/澄清规格后 ===
# coding: ascii
def ranks_from_scores(sorted_scores):
"""sorted_scores: a list of tuples (object_id, score), sorted by score DESCENDING
return a mapping of object IDs to ranks
"""
ranks = {}
previous_score = object()
for index, (obj_id, score) in enumerate(sorted_scores):
if score != previous_score:
previous_score = score
rank = index + 1
ranks[obj_id] = rank
return ranks
from operator import itemgetter
import pprint
scores0 = dict([
('Apolo Ohno', 0),
('Shanie Davis', -1),
('Bodie Miller', -2),
('Lindsay Vohn', -3),
('Shawn White', -3)
])
scores1 = {
'lorem': 100,
'ipsum': 200,
'dolor': 300,
'sit': 300,
'amet': 300,
'quia': 400,
'consectetur': 500,
'adipiscing': 500,
'elit': 600,
}
scores2 = {
'lorem': 100,
'ipsum': 200,
'dolor': 300,
'sit': 300,
'amet': 300,
'quia': 400,
'consectetur': 500,
'adipiscing': 500,
'elit': 6000,
}
import pprint
funcs = (ranks_from_scores, ) # Watch this space!
tests = (scores0, scores1, scores2)
for test in tests:
print
test_list = sorted(test.items(), key=itemgetter(1), reverse=True)
print "Input:", test_list
for func in funcs:
result = func(test_list)
print "%s ->" % func.__name__
pprint.pprint(result)
结果:
Input: [('Apolo Ohno', 0), ('Shanie Davis', -1), ('Bodie Miller', -2), ('Lindsay
Vohn', -3), ('Shawn White', -3)]
ranks_from_scores ->
{'Apolo Ohno': 1,
'Bodie Miller': 3,
'Lindsay Vohn': 4,
'Shanie Davis': 2,
'Shawn White': 4}
Input: [('elit', 600), ('consectetur', 500), ('adipiscing', 500), ('quia', 400),
('dolor', 300), ('sit', 300), ('amet', 300), ('ipsum', 200), ('lorem', 100)]
ranks_from_scores ->
{'adipiscing': 2,
'amet': 5,
'consectetur': 2,
'dolor': 5,
'elit': 1,
'ipsum': 8,
'lorem': 9,
'quia': 4,
'sit': 5}
Input: [('elit', 6000), ('consectetur', 500), ('adipiscing', 500), ('quia', 400)
, ('dolor', 300), ('sit', 300), ('amet', 300), ('ipsum', 200), ('lorem', 100)]
ranks_from_scores ->
{'adipiscing': 2,
'amet': 5,
'consectetur': 2,
'dolor': 5,
'elit': 1,
'ipsum': 8,
'lorem': 9,
'quia': 4,
'sit': 5}
=== 原始提交 ===
这段代码假设你希望得分最高的得到第一名,而不是得分最低的得到第一名(或者是0分!)。
# coding: ascii
def ranks_from_scores(scores, debug=False):
"""scores (a mapping of object IDs to scores)
return a mapping of object IDs to ranks
"""
alist = [(v, k) for k, v in scores.items()]
alist.sort(reverse=True)
if debug: print 'alist:', alist
bdict = {}
previous_score = object()
for posn, (score, obj_id) in enumerate(alist):
if score != previous_score:
previous_score = score
rank = posn + 1
bdict[obj_id] = rank
if debug:
print 'bdict:', bdict
blist = [(v, k) for k, v in bdict.items()]
print 'blist:', sorted(blist)
return bdict
ranks_from_scores(
{'q': 10, 'w': 20, 'e': 20, 'r': 20, 't': 30},
debug=True,
)
输出:
alist: [(30, 't'), (20, 'w'), (20, 'r'), (20, 'e'), (10, 'q')]
bdict: {'q': 5, 'r': 2, 'e': 2, 't': 1, 'w': 2}
blist: [(1, 't'), (2, 'e'), (2, 'r'), (2, 'w'), (5, 'q')]
8
>>> sorted_scores = [
... ('Apolo Ohno', 0),
... ('Shanie Davis', -1),
... ('Bodie Miller', -2),
... ('Lindsay Vohn', -3),
... ('Shawn White', -3),
... ('Bryan Veloso',-4)
... ]
>>>
>>> res = {}
>>> prev = None
>>> for i,(k,v) in enumerate(sorted_scores):
... if v!=prev:
... place,prev = i+1,v
... res[k] = place
...
>>> print res
{'Apolo Ohno': 1, 'Bryan Veloso': 6, 'Shanie Davis': 2, 'Lindsay Vohn': 4, 'Bodie Miller': 3, 'Shawn White': 4}
>>> from operator import itemgetter
>>> print sorted(res.items(),key=itemgetter(1))
[('Apolo Ohno', 1), ('Shanie Davis', 2), ('Bodie Miller', 3), ('Lindsay Vohn', 4), ('Shawn White', 4), ('Bryan Veloso', 6)]
记住,字典是没有顺序的,所以如果你想按照顺序来遍历它,你需要这样做