Python 3.x实现无跳号密集排序(Dense Rank)技术问询
解决Python密集排序(Dense Rank)跳号问题
我来帮你搞定这个密集排序的问题!你当前代码里用sorted_scores.index(x)的问题在于,它会返回每个分数第一次出现的索引,所以重复分数后面的元素会直接跳过中间的位置,导致排名跳号。比如两个71分的元素,第一个在索引5,第二个也取5,排名都是6,但下一个67分的第一个位置是索引7,所以排名变成8,就跳过了7,这就是你看到的跳号情况。
下面给你两种简洁的实现方式,都能得到你想要的无跳号排名:
方法一:遍历跟踪法(直观易懂)
这种方法直接遍历排序后的列表,维护当前排名和上一个分数,遇到相同分数就保持排名,不同分数就递增排名:
rank_list = [{'Id': 236966, 'score': 91.0}, {'Id': 237241, 'score': 82.0}, {'Id': 237077, 'score': 79.0}, {'Id': 237084, 'score': 78.0}, {'Id': 237080, 'score': 72.0}, {'Id': 237236, 'score': 71.0}, {'Id': 236979, 'score': 71.0}, {'Id': 236909, 'score': 67.0}, {'Id': 237174, 'score': 67.0}, {'Id': 237035, 'score': 66.0}] # 先按分数降序排序(你原来的排序逻辑是对的) rank_list.sort(key=lambda e: e['score'], reverse=True) current_rank = 1 previous_score = rank_list[0]['score'] rank_list[0]['rank'] = current_rank for obj in rank_list[1:]: if obj['score'] == previous_score: # 分数相同,排名不变 obj['rank'] = current_rank else: # 分数不同,排名递增 current_rank += 1 obj['rank'] = current_rank previous_score = obj['score'] # 验证输出 for item in rank_list: print(item)
运行后每个元素的rank就是[1, 2, 3, 4, 5, 6, 6, 7, 7, 8],完全符合你的期望。
方法二:字典映射法(适合大数据量)
如果你的列表很大,遍历一次生成分数到排名的映射,再批量赋值会更高效:
rank_list = [{'Id': 236966, 'score': 91.0}, {'Id': 237241, 'score': 82.0}, {'Id': 237077, 'score': 79.0}, {'Id': 237084, 'score': 78.0}, {'Id': 237080, 'score': 72.0}, {'Id': 237236, 'score': 71.0}, {'Id': 236979, 'score': 71.0}, {'Id': 236909, 'score': 67.0}, {'Id': 237174, 'score': 67.0}, {'Id': 237035, 'score': 66.0}] rank_list.sort(key=lambda e: e['score'], reverse=True) # 生成唯一分数的排序列表,然后创建分数到密集排名的映射 unique_scores = sorted({obj['score'] for obj in rank_list}, reverse=True) score_rank_map = {score: idx+1 for idx, score in enumerate(unique_scores)} # 给每个元素赋值rank for obj in rank_list: obj['rank'] = score_rank_map[obj['score']] # 验证输出 for item in rank_list: print(item)
这个方法先提取所有唯一的分数并降序排序,然后给每个唯一分数分配连续的排名,最后直接通过字典映射给每个元素赋值,逻辑也很清晰,而且大数据量下比遍历判断的方式更快。
这两种方法都能解决你当前的跳号问题,你可以根据自己的需求选择~
内容的提问来源于stack exchange,提问作者monicak




