@@ -74,15 +74,27 @@ def load_tournament_data(log_dir: Path) -> pd.DataFrame:
7474 return pd .DataFrame (data )
7575
7676
77- def calculate_streak_probabilities (df : pd .DataFrame ) -> tuple [pd .DataFrame , pd .DataFrame ]:
77+ def calculate_streak_probabilities (df : pd .DataFrame ) -> tuple [pd .DataFrame , pd .DataFrame , pd . DataFrame ]:
7878 """Calculate comeback and falldown probabilities after i consecutive losses/wins."""
7979 model_comeback_stats = defaultdict (lambda : defaultdict (lambda : {"opportunities" : 0 , "successes" : 0 }))
8080 model_falldown_stats = defaultdict (lambda : defaultdict (lambda : {"opportunities" : 0 , "successes" : 0 }))
81+ model_overall_stats = defaultdict (lambda : {"total_rounds" : 0 , "wins" : 0 })
8182
8283 for _ , row in df .iterrows ():
8384 model_a = row ["model_a" ]
8485 model_b = row ["model_b" ]
8586
87+ for round_num in range (1 , 16 ):
88+ winner_current = row [f"round_{ round_num } _winner" ]
89+
90+ # Track overall win rates
91+ model_overall_stats [model_a ]["total_rounds" ] += 1
92+ model_overall_stats [model_b ]["total_rounds" ] += 1
93+ if winner_current == model_a :
94+ model_overall_stats [model_a ]["wins" ] += 1
95+ elif winner_current == model_b :
96+ model_overall_stats [model_b ]["wins" ] += 1
97+
8698 for round_num in range (2 , 16 ):
8799 winner_current = row [f"round_{ round_num } _winner" ]
88100
@@ -171,10 +183,21 @@ def calculate_streak_probabilities(df: pd.DataFrame) -> tuple[pd.DataFrame, pd.D
171183
172184 falldown_prob_df = pd .DataFrame (falldown_prob_data ).set_index ("model" )
173185
174- return comeback_prob_df , falldown_prob_df
186+ # Create overall win rate dataframe
187+ overall_win_rate_data = []
188+ for model in model_overall_stats :
189+ if model_overall_stats [model ]["total_rounds" ] > 0 :
190+ win_rate = model_overall_stats [model ]["wins" ] / model_overall_stats [model ]["total_rounds" ]
191+ overall_win_rate_data .append ({"model" : model , "win_rate" : win_rate })
192+
193+ overall_win_rate_df = pd .DataFrame (overall_win_rate_data ).set_index ("model" )
175194
195+ return comeback_prob_df , falldown_prob_df , overall_win_rate_df
176196
177- def plot_comeback_probabilities (comeback_prob_df : pd .DataFrame , output_file : Path ) -> None :
197+
198+ def plot_comeback_probabilities (
199+ comeback_prob_df : pd .DataFrame , overall_win_rate_df : pd .DataFrame , output_file : Path
200+ ) -> None :
178201 """Plot probability of winning after i consecutive losses."""
179202 fig , ax = plt .subplots (figsize = (6 , 6 ))
180203 label_font = FontProperties (fname = FONT_BOLD .get_file (), size = 18 )
@@ -186,6 +209,7 @@ def plot_comeback_probabilities(comeback_prob_df: pd.DataFrame, output_file: Pat
186209 for idx , model in enumerate (sorted_models ):
187210 x_values = []
188211 y_values = []
212+
189213 for i in range (1 , 15 ):
190214 col = f"comeback_prob_after_{ i } _losses"
191215 if pd .notna (comeback_prob_df .loc [model , col ]):
@@ -194,6 +218,12 @@ def plot_comeback_probabilities(comeback_prob_df: pd.DataFrame, output_file: Pat
194218
195219 if x_values :
196220 display_name = MODEL_TO_DISPLAY_NAME .get (model , model )
221+
222+ # Add overall win rate to legend
223+ if model in overall_win_rate_df .index :
224+ win_rate_pct = overall_win_rate_df .loc [model , "win_rate" ] * 100
225+ display_name = f"{ display_name } ({ win_rate_pct :.0f} %)"
226+
197227 color = MODEL_TO_COLOR .get (model , None )
198228 marker = MARKERS [idx % len (MARKERS )]
199229 ax .plot (
@@ -214,14 +244,20 @@ def plot_comeback_probabilities(comeback_prob_df: pd.DataFrame, output_file: Pat
214244 ax .grid (True , alpha = 0.3 )
215245 ax .yaxis .set_minor_locator (AutoMinorLocator ())
216246 ax .xaxis .set_minor_locator (NullLocator ())
247+
248+ # Set x-axis to show integer ticks
249+ ax .set_xticks (range (1 , 15 ))
250+ ax .set_xlim (0.5 , max (ax .get_xlim ()[1 ], 14.5 ))
217251 for label in ax .get_xticklabels () + ax .get_yticklabels ():
218252 label .set_fontproperties (FontProperties (fname = FONT_BOLD .get_file (), size = 14 ))
219253 plt .tight_layout ()
220254 plt .savefig (output_file , bbox_inches = "tight" )
221255 print (f"Saved comeback probability plot to { output_file } " )
222256
223257
224- def plot_falldown_probabilities (falldown_prob_df : pd .DataFrame , output_file : Path ) -> None :
258+ def plot_falldown_probabilities (
259+ falldown_prob_df : pd .DataFrame , overall_win_rate_df : pd .DataFrame , output_file : Path
260+ ) -> None :
225261 """Plot probability of losing after i consecutive wins."""
226262 fig , ax = plt .subplots (figsize = (6 , 6 ))
227263 label_font = FontProperties (fname = FONT_BOLD .get_file (), size = 18 )
@@ -233,6 +269,7 @@ def plot_falldown_probabilities(falldown_prob_df: pd.DataFrame, output_file: Pat
233269 for idx , model in enumerate (sorted_models ):
234270 x_values = []
235271 y_values = []
272+
236273 for i in range (1 , 15 ):
237274 col = f"falldown_prob_after_{ i } _wins"
238275 if pd .notna (falldown_prob_df .loc [model , col ]):
@@ -241,6 +278,12 @@ def plot_falldown_probabilities(falldown_prob_df: pd.DataFrame, output_file: Pat
241278
242279 if x_values :
243280 display_name = MODEL_TO_DISPLAY_NAME .get (model , model )
281+
282+ # Add overall loss rate to legend
283+ if model in overall_win_rate_df .index :
284+ loss_rate_pct = (1 - overall_win_rate_df .loc [model , "win_rate" ]) * 100
285+ display_name = f"{ display_name } ({ loss_rate_pct :.0f} %)"
286+
244287 color = MODEL_TO_COLOR .get (model , None )
245288 marker = MARKERS [idx % len (MARKERS )]
246289 ax .plot (
@@ -261,6 +304,10 @@ def plot_falldown_probabilities(falldown_prob_df: pd.DataFrame, output_file: Pat
261304 ax .grid (True , alpha = 0.3 )
262305 ax .yaxis .set_minor_locator (AutoMinorLocator ())
263306 ax .xaxis .set_minor_locator (NullLocator ())
307+
308+ # Set x-axis to show integer ticks
309+ ax .set_xticks (range (1 , 15 ))
310+ ax .set_xlim (0.5 , max (ax .get_xlim ()[1 ], 14.5 ))
264311 for label in ax .get_xticklabels () + ax .get_yticklabels ():
265312 label .set_fontproperties (FontProperties (fname = FONT_BOLD .get_file (), size = 14 ))
266313 plt .tight_layout ()
@@ -277,10 +324,10 @@ def main(log_dir: Path | None = None) -> None:
277324 df = load_tournament_data (log_dir )
278325 print (f"Loaded { len (df )} tournaments" )
279326
280- comeback_prob_df , falldown_prob_df = calculate_streak_probabilities (df )
327+ comeback_prob_df , falldown_prob_df , overall_win_rate_df = calculate_streak_probabilities (df )
281328
282- plot_comeback_probabilities (comeback_prob_df , ASSETS_DIR / "comeback_probabilities.pdf" )
283- plot_falldown_probabilities (falldown_prob_df , ASSETS_DIR / "falldown_probabilities.pdf" )
329+ plot_comeback_probabilities (comeback_prob_df , overall_win_rate_df , ASSETS_DIR / "comeback_probabilities.pdf" )
330+ plot_falldown_probabilities (falldown_prob_df , overall_win_rate_df , ASSETS_DIR / "falldown_probabilities.pdf" )
284331
285332
286333if __name__ == "__main__" :
0 commit comments