| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647 |
- """跨业务模块复用的排名归一化工具。"""
- from __future__ import annotations
- from collections.abc import Hashable
- from typing import TypeVar
- KeyT = TypeVar("KeyT", bound=Hashable)
- def rank_with_scores(items: list[tuple[KeyT, float]]) -> dict[KeyT, dict[str, float | int]]:
- """按值降序排名,并返回同分平均名次与 ``(0, 1]`` 排名分。
- 不同量纲的数据应先分别调用本函数,不能先把原始值相加。排名分公式与
- ``category_tree_weight`` 的四维全局排名口径一致:
- ``score = (n - avg_rank + 1) / n``。
- """
- if not items:
- return {}
- sorted_items = sorted(items, key=lambda item: (-item[1], str(item[0])))
- total = len(sorted_items)
- result: dict[KeyT, dict[str, float | int]] = {}
- start = 0
- while start < total:
- end = start
- value = sorted_items[start][1]
- while end < total and sorted_items[end][1] == value:
- end += 1
- average_rank = (start + 1 + end) / 2.0
- normalized_score = (total - average_rank + 1) / total
- for index in range(start, end):
- result[sorted_items[index][0]] = {
- "rank": average_rank,
- "total": total,
- "normalized_score": normalized_score,
- }
- start = end
- return result
- def rank_to_scores(items: list[tuple[KeyT, float]]) -> dict[KeyT, float]:
- """兼容原调用方:只返回排名归一分。"""
- return {
- key: float(position["normalized_score"])
- for key, position in rank_with_scores(items).items()
- }
|