|
| 1 | +import io |
1 | 2 | import json |
2 | 3 | import subprocess |
3 | 4 | import tempfile |
@@ -102,6 +103,10 @@ def __init__(self, bot: "ClusterBot"): |
102 | 103 | name="show-stats", description="Show stats for the kernelbot" |
103 | 104 | )(self.show_bot_stats) |
104 | 105 |
|
| 106 | + self.show_timing_correlation = bot.admin_group.command( |
| 107 | + name="show-timing-correlation", description="Show correlation between public and private run timings" |
| 108 | + )(self.show_timing_correlation) |
| 109 | + |
105 | 110 | self.resync = bot.admin_group.command( |
106 | 111 | name="resync", description="Trigger re-synchronization of slash commands" |
107 | 112 | )(self.resync) |
@@ -793,6 +798,67 @@ async def show_bot_stats(self, interaction: discord.Interaction, last_day_only: |
793 | 798 | msg += "\n```" |
794 | 799 | await send_discord_message(interaction, msg, ephemeral=True) |
795 | 800 |
|
| 801 | + @with_error_handling |
| 802 | + @discord.app_commands.describe(leaderboard="Which leaderboard") |
| 803 | + @discord.app_commands.autocomplete(leaderboard=leaderboard_name_autocomplete) |
| 804 | + async def show_timing_correlation(self, interaction: discord.Interaction, leaderboard: str): |
| 805 | + is_admin = await self.admin_check(interaction) |
| 806 | + if not is_admin: |
| 807 | + await send_discord_message( |
| 808 | + interaction, |
| 809 | + "You need to have Admin permissions to run this command", |
| 810 | + ephemeral=True, |
| 811 | + ) |
| 812 | + return |
| 813 | + |
| 814 | + with self.bot.leaderboard_db as db: |
| 815 | + stats = db.generate_correlation_stats(leaderboard) |
| 816 | + |
| 817 | + if len(stats) == 0: |
| 818 | + await send_discord_message( |
| 819 | + interaction, |
| 820 | + f"No ranked runs (with matching public/secret scores) found for leaderboard {leaderboard}.") |
| 821 | + return |
| 822 | + |
| 823 | + import matplotlib.pyplot as plt |
| 824 | + import numpy as np |
| 825 | + |
| 826 | + files = [] |
| 827 | + for runner, pairs in stats.items(): |
| 828 | + public_scores, secret_scores = zip(*pairs) |
| 829 | + |
| 830 | + plt.figure(figsize=(8, 6)) |
| 831 | + plt.scatter(public_scores, secret_scores, alpha=0.6) |
| 832 | + |
| 833 | + # Diagonal reference line |
| 834 | + min_val = min(min(public_scores), min(secret_scores)) |
| 835 | + max_val = max(max(public_scores), max(secret_scores)) |
| 836 | + plt.plot([min_val, max_val], [min_val, max_val], 'r--', alpha=0.5, label='y=x') |
| 837 | + |
| 838 | + # Calculate correlation |
| 839 | + corr = np.corrcoef(public_scores, secret_scores)[0, 1] |
| 840 | + |
| 841 | + plt.xlabel('Public Score') |
| 842 | + plt.ylabel('Secret Score') |
| 843 | + plt.title(f'{leaderboard} on {runner} - Correlation: {corr:.3f}') |
| 844 | + plt.legend() |
| 845 | + plt.grid(True, alpha=0.3) |
| 846 | + |
| 847 | + plt.tight_layout() |
| 848 | + |
| 849 | + buf = io.BytesIO() |
| 850 | + plt.savefig(buf, format='png', dpi=150) |
| 851 | + buf.seek(0) |
| 852 | + |
| 853 | + files.append(discord.File(buf, filename=f'correlation_{runner}.png')) |
| 854 | + plt.close() |
| 855 | + |
| 856 | + await send_discord_message( |
| 857 | + interaction, |
| 858 | + "Correlation of running times for public and private runs", |
| 859 | + files=files, |
| 860 | + ephemeral=True) |
| 861 | + |
796 | 862 | @with_error_handling |
797 | 863 | async def resync(self, interaction: discord.Interaction): |
798 | 864 | """Admin command to resync slash commands""" |
|
0 commit comments