Skip to content

Commit ca75807

Browse files
committed
Fix unordered mu diffs when using scores
1 parent 92d4208 commit ca75807

13 files changed

+663
-702
lines changed

benchmark/benchmark.py

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
- BradleyTerryFull
1010
- BradleyTerryPart
1111
12-
Each system is tested with margin=0.0 and margin=2.0 to evaluate the impact on prediction accuracy.
12+
Each system is tested with margin=0.0 and margin=1.0 to evaluate the impact on prediction accuracy.
1313
"""
1414

1515
import gc
@@ -79,7 +79,7 @@ def __init__(
7979
}
8080

8181
# Define margins to test
82-
self.margins = [0.0, 2.0]
82+
self.margins = [0.0, 1.0]
8383

8484
def load_data(self) -> None:
8585
"""
@@ -391,7 +391,7 @@ def display_results(
391391
table.add_column("Avg Time (s)", style="yellow")
392392

393393
# Add a separate table for margin difference analysis
394-
diff_table = Table(title="Margin Impact Analysis (2.0 vs 0.0)")
394+
diff_table = Table(title="Margin Impact Analysis")
395395
diff_table.add_column("Model", style="cyan")
396396
diff_table.add_column("Accuracy Difference", style="magenta")
397397
diff_table.add_column("Speed Difference", style="yellow")
@@ -412,19 +412,27 @@ def display_results(
412412
f"{metrics[3]:.2f}",
413413
)
414414

415-
# Calculate the difference between margin=2.0 and margin=0.0
416-
if 0.0 in metrics_by_margin and 2.0 in metrics_by_margin:
417-
m0 = metrics_by_margin[0.0]
418-
m2 = metrics_by_margin[2.0]
415+
# Calculate the difference between margins automatically
416+
if len(self.margins) >= 2:
417+
# Sort margins to get consistent comparison (highest vs lowest)
418+
sorted_margins = sorted(self.margins)
419+
low_margin = sorted_margins[0]
420+
high_margin = sorted_margins[-1]
419421

420-
acc_diff = m2[2] - m0[2]
421-
time_diff = (m2[3] / m0[3] - 1.0) * 100
422+
if low_margin in metrics_by_margin and high_margin in metrics_by_margin:
423+
m_low = metrics_by_margin[low_margin]
424+
m_high = metrics_by_margin[high_margin]
422425

423-
diff_table.add_row(
424-
model_name,
425-
f"{acc_diff:.2f}% {'better' if acc_diff > 0 else 'worse'}",
426-
f"{abs(time_diff):.2f}% {'slower' if time_diff > 0 else 'faster'}",
427-
)
426+
acc_diff = m_high[2] - m_low[2]
427+
time_diff = (
428+
(m_high[3] / m_low[3] - 1.0) * 100 if m_low[3] > 0 else 0
429+
)
430+
431+
diff_table.add_row(
432+
model_name,
433+
f"{acc_diff:+.2f}% ({'better' if acc_diff > 0 else 'worse' if acc_diff < 0 else 'same'})",
434+
f"{abs(time_diff):.2f}% {'slower' if time_diff > 0 else 'faster' if time_diff < 0 else 'same'}",
435+
)
428436

429437
# Print tables
430438
self.console.print(table)

openskill/models/weng_lin/bradley_terry_full.py

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ def rate(
607607

608608
# Convert Score to Ranks
609609
if not ranks and scores:
610-
ranks = [-s for s in scores]
610+
ranks = list(map(lambda s: -s, scores))
611611
ranks = self._calculate_rankings(teams, ranks)
612612

613613
# Normalize Weights
@@ -1056,8 +1056,7 @@ def _calculate_rankings(
10561056
Calculates the rankings based on the scores or ranks of the teams.
10571057
10581058
It assigns a rank to each team based on their score, with the team with
1059-
the highest score being ranked first. Ties are broken by a team's prior
1060-
averaged :math:`mu` values.
1059+
the highest score being ranked first.
10611060
10621061
:param game: A list of teams, where teams are lists of
10631062
:class:`BradleyTerryFullRating` objects.
@@ -1069,36 +1068,16 @@ def _calculate_rankings(
10691068
if not game:
10701069
return []
10711070

1072-
if ranks is None:
1073-
return list(range(len(game)))
1074-
1075-
team_data = []
1076-
for index, rank in enumerate(ranks):
1077-
team = game[index]
1078-
average_ordinal = sum(player.ordinal() for player in team) / len(team)
1079-
team_data.append((rank, average_ordinal, index))
1080-
1081-
# Lower Ranks (Better), Higher Skill (Breaks Ties)
1082-
team_data.sort(key=lambda data: (data[0], -data[1])) # Rank, -(Average Skill)
1083-
1084-
# Assign Final Ranks: Preserve Ranks for Identical Skill
1085-
final_ranks = [0.0] * len(game)
1086-
current_rank = 0
1087-
1088-
for team_index, (
1089-
original_rank,
1090-
average_ordinal,
1091-
original_team_index,
1092-
) in enumerate(team_data):
1093-
if team_index > 0:
1094-
preceding_original_rank, preceding_average_ordinal, _ = team_data[
1095-
team_index - 1
1096-
]
1097-
# Advance Rank: Performance Changes OR Rank Changes
1098-
if (
1099-
original_rank != preceding_original_rank
1100-
or average_ordinal != preceding_average_ordinal
1101-
):
1102-
current_rank = original_team_index
1103-
final_ranks[original_team_index] = float(current_rank)
1104-
return final_ranks
1071+
if ranks:
1072+
team_scores = [ranks[i] or i for i, _ in enumerate(game)]
1073+
else:
1074+
team_scores = [i for i, _ in enumerate(game)]
1075+
1076+
output_ranks: dict[int, float] = {}
1077+
s = 0
1078+
for index, value in enumerate(team_scores):
1079+
if index > 0:
1080+
if team_scores[index - 1] < team_scores[index]:
1081+
s = index
1082+
output_ranks[index] = s
1083+
return list(output_ranks.values())

openskill/models/weng_lin/bradley_terry_part.py

Lines changed: 15 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,7 @@ def rate(
617617

618618
# Convert Score to Ranks
619619
if not ranks and scores:
620-
ranks = [-s for s in scores]
620+
ranks = list(map(lambda s: -s, scores))
621621
ranks = self._calculate_rankings(teams, ranks)
622622

623623
# Normalize Weights
@@ -1052,8 +1052,7 @@ def _calculate_rankings(
10521052
Calculates the rankings based on the scores or ranks of the teams.
10531053
10541054
It assigns a rank to each team based on their score, with the team with
1055-
the highest score being ranked first. Ties are broken by a team's prior
1056-
averaged :math:`mu` values.
1055+
the highest score being ranked first.
10571056
10581057
:param game: A list of teams, where teams are lists of
10591058
:class:`BradleyTerryPartRating` objects.
@@ -1065,36 +1064,16 @@ def _calculate_rankings(
10651064
if not game:
10661065
return []
10671066

1068-
if ranks is None:
1069-
return list(range(len(game)))
1070-
1071-
team_data = []
1072-
for index, rank in enumerate(ranks):
1073-
team = game[index]
1074-
average_ordinal = sum(player.ordinal() for player in team) / len(team)
1075-
team_data.append((rank, average_ordinal, index))
1076-
1077-
# Lower Ranks (Better), Higher Skill (Breaks Ties)
1078-
team_data.sort(key=lambda data: (data[0], -data[1])) # Rank, -(Average Skill)
1079-
1080-
# Assign Final Ranks: Preserve Ranks for Identical Skill
1081-
final_ranks = [0.0] * len(game)
1082-
current_rank = 0
1083-
1084-
for team_index, (
1085-
original_rank,
1086-
average_ordinal,
1087-
original_team_index,
1088-
) in enumerate(team_data):
1089-
if team_index > 0:
1090-
preceding_original_rank, preceding_average_ordinal, _ = team_data[
1091-
team_index - 1
1092-
]
1093-
# Advance Rank: Performance Changes OR Rank Changes
1094-
if (
1095-
original_rank != preceding_original_rank
1096-
or average_ordinal != preceding_average_ordinal
1097-
):
1098-
current_rank = original_team_index
1099-
final_ranks[original_team_index] = float(current_rank)
1100-
return final_ranks
1067+
if ranks:
1068+
team_scores = [ranks[i] or i for i, _ in enumerate(game)]
1069+
else:
1070+
team_scores = [i for i, _ in enumerate(game)]
1071+
1072+
output_ranks: dict[int, float] = {}
1073+
s = 0
1074+
for index, value in enumerate(team_scores):
1075+
if index > 0:
1076+
if team_scores[index - 1] < team_scores[index]:
1077+
s = index
1078+
output_ranks[index] = s
1079+
return list(output_ranks.values())

0 commit comments

Comments
 (0)