(我很抱歉此问题的先前版本显示了我需要解决的错误功能,这已得到纠正,我希望这个问题现在更有意义。)
我有一个有分数的对象列表,我正在尝试根据这些分数为他们分配排名。以下基本上是我输出数据的方式。
sorted_scores = [
('Apolo Ohno', 0),
('Shanie Davis', -1),
('Bodie Miller', -2),
('Lindsay Vohn', -3),
('Shawn White', -3),
('Bryan Veloso', -4)
]
我有领带。现在,将位置分配给上面的对象的函数现在是一个简单的for循环,只是将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>
提前致谢。 :)
答案 0 :(得分:7)
>>> 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}
请记住,dicts是无序的,所以要按地方顺序迭代,你需要这样做
>>> 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)]
答案 1 :(得分:3)
这样做的方法不是计算元素的位置是一些任意的序列,而是计算有多少其他元素有更好的分数。
修改强>
受欢迎的需求,O(n)'ed和一切:
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
答案 2 :(得分: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}
===原始提交===
此代码假设您确实希望最高分获得排名1,而不是获得排名1(或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')]
答案 3 :(得分:1)
您似乎可以使用sorted
和enumerate
内置,groupby
itertools
方法和itemgetter
operator
方法。假设分数越高越好......(如果分数越低越好,将reverse=True
更改为reverse=False
)
>>> from itertools import groupby
>>> from operator import itemgetter
>>> scores = {
... 'lorem': 100,
... 'ipsum': 200,
... 'dolor': 300,
... 'sit': 300,
... 'amet': 300,
... 'quia': 400,
... 'consectetur': 500,
... 'adipiscing': 500,
... 'elit': 600,
... }
>>> sorted_items = sorted(scores.items(), key=itemgetter(1), reverse=True)
>>> groups = groupby(sorted_items, itemgetter(1))
>>> for rank, (score, items) in enumerate(groups):
... print rank+1, map(itemgetter(0), items)
...
1 ['elit']
2 ['consectetur', 'adipiscing']
3 ['quia']
4 ['dolor', 'sit', 'amet']
5 ['ipsum']
6 ['lorem']
答案 4 :(得分:1)
<强>解决方案强>
这是通过稍微修改代码而不是导入模块来实现此目的的一种简单方法:
prev = None
rank = 0
incr = 1
positions = {}
for key, value in sorted_list:
if value is not None:
if value != prev:
rank += incr
incr = 1
else:
incr += 1
positions[key] = rank
prev = value
测试
有关
sorted_list = [
('Apolo Ohno', 0),
('Shanie Davis', -1),
('Bodie Miller', -2),
('Lindsay Vohn', -3),
('Shawn White', -3),
('Bryan Veloso',-4)
]
我得到的职位是:
{'Apolo Ohno': 1,
'Shanie Davis': 2,
'Bodie Miller': 3,
'Lindsay Vohn': 4,
'Shawn White': 4,
'Bryan Veloso': 6}
我认为即使你不清楚两个4之后是否应该有6个,我认为你的目标是什么。
答案 5 :(得分:1)
这是一种将一些其他解决方案的各个方面结合到灵活的生成器功能中的方法。
def rank_sorted(sequence, start=1, key=None, reverse=True):
"""A combination of `enumerate` and `sorted` iterators that deals
with tied ranks.
"""
previous_value = object() # won't compare equal to anything
sorted_iterator = sorted(sequence, key=key, reverse=reverse)
for index, item in enumerate(sorted_iterator, start=start):
# use key function to choose value if given
if key is None:
value = item
else:
value = key(item)
# only update rank when sort value changes
if value != previous_value:
previous_value = value
rank = index
yield rank, item
您可以使用start
,key
和reverse
的不同值调用,以允许排名从0或1开始,以传递自定义键功能(如{{1按值排序字典),并轻松切换到较低的分数被认为是更高的排名。使用原始问题中的示例:
itemgetter(1)
答案 6 :(得分:1)
与最佳答案相同,只是保留常规内容而不是跳到排名的下一个位置: 即:排名= [1,2,3,4,4,5]而不是排名= [1,2,3,4,4,6]
sorted_scores = [
('Apolo Ohno', 0),
('Shanie Davis', -1),
('Bodie Miller', -2),
('Lindsay Vohn', -3),
('Shawn White', -3),
('Bryan Veloso',-4)
]
res = {}
prev = None
n = 0
for k,v in sorted_scores:
if v!=prev:
n +=1
place,prev = n,v
res[k] = place
print (res)
{'Apolo Ohno': 1, 'Shanie Davis': 2, 'Bodie Miller': 3, 'Lindsay Vohn': 4, 'Shawn White': 4, 'Bryan Veloso': 5}
答案 7 :(得分:0)
我不得不对你想做什么做出一系列假设,但这是一次尝试:
scores = {
'lorem': 100,
'ipsum': 200,
'dolor': 300,
'sit': 300,
'amet': 300,
'quia': 400,
'consectetur': 500,
'adipiscing': 500,
'elit': 600,
}
groups = {}
for (member, score) in scores.items():
if score not in groups:
groups[score] = [member]
else:
groups[score].append(member)
positions = {}
for (rank, (score, members)) in enumerate(groups.items()):
for member in members:
positions[member] = rank
扩展细节,以显示工作:
>>> import pprint
>>> scores = {
... 'lorem': 100,
... 'ipsum': 200,
... 'dolor': 300,
... 'sit': 300,
... 'amet': 300,
... 'quia': 400,
... 'consectetur': 500,
... 'adipiscing': 500,
... 'elit': 600,
... }
>>> groups = {}
>>> for (member, score) in scores.items():
... if score not in groups:
... groups[score] = [member]
... else:
... groups[score].append(member)
...
>>> pprint.pprint(groups)
{100: ['lorem'],
200: ['ipsum'],
300: ['sit', 'dolor', 'amet'],
400: ['quia'],
500: ['consectetur', 'adipiscing'],
600: ['elit']}
>>> positions = {}
>>> for (rank, (score, members)) in enumerate(groups.items()):
... for member in members:
... positions[member] = rank
...
>>> pprint.pprint(positions)
{'adipiscing': 4,
'amet': 2,
'consectetur': 4,
'dolor': 2,
'elit': 5,
'ipsum': 1,
'lorem': 0,
'quia': 3,
'sit': 2}
>>> pprint.pprint(sorted(positions.items(), key=lambda i: i[1]))
[('lorem', 0),
('ipsum', 1),
('sit', 2),
('dolor', 2),
('amet', 2),
('quia', 3),
('consectetur', 4),
('adipiscing', 4),
('elit', 5)]
答案 8 :(得分:0)
这是一种简单的方法
last = None
position = 0
delta = 1
for key, value in sorted_list:
if value is not None:
if value != last:
position += delta
delta = 1
else:
delta += 1
# i believe this is supposed to be [key] not [value] in OP's code
positions[key] = position
last = value
答案 9 :(得分:0)
>>> sorted_scores = [
... ('Apolo Ohno', 0),
... ('Shanie Davis', -1),
... ('Bodie Miller', -2),
... ('Lindsay Vohn', -3),
... ('Shawn White', -3),
... ('Bryan Veloso',-4)
... ]
>>>
>>> from itertools import groupby
>>> from operator import itemgetter
>>>
>>> place=1
>>> res={}
>>> for _,items in groupby(sorted_scores,key=itemgetter(1)):
... for i,item in enumerate(items):
... res[item[0]]= place
... place+=i+1
...
>>> print res
{'Apolo Ohno': 1, 'Bryan Veloso': 6, 'Shanie Davis': 2, 'Lindsay Vohn': 4, 'Bodie Miller': 3, 'Shawn White': 4}
请记住,dicts是无序的,所以要按地方顺序迭代,你需要这样做
>>> 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)]