import numpy as np import pandas as pd import matplotlib matplotlib.use("Agg") import matplotlib.pyplot as plt from typing import Optional class Plotter: def __init__(self, data_loader): self.data_loader = data_loader def create_comparison_plot( self, model_filter: str, open_source_filter: str, year_filter: str, selected_plot_metric: str, plot_sort_mode: str, display_metric_name: Optional[str] = None, ) -> plt.Figure: """创建对比图 - 单指标多模型对比""" metric_display_name = display_metric_name or selected_plot_metric df = self.data_loader.df_all if df is None or df.empty: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, "No data available for plotting", ha="center", va="center", fontsize=14) ax.axis("off") return fig # 应用筛选条件 if model_filter and model_filter.strip(): df = df[df["Model"].str.contains(model_filter, case=False, na=False)] if open_source_filter and open_source_filter != "All": df = df[df["open_source"] == open_source_filter] if year_filter and year_filter != "All": df = df[df["year"] == year_filter] if df.empty: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, "No models match the filter criteria", ha="center", va="center", fontsize=14) ax.axis("off") return fig if not selected_plot_metric: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, "Please select a metric", ha="center", va="center", fontsize=14) ax.axis("off") return fig # 检查指标是否存在 if selected_plot_metric not in df.columns: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, f"Metric '{selected_plot_metric}' not found", ha="center", va="center", fontsize=14) ax.axis("off") return fig # 设置中文字体 plt.rcParams['font.sans-serif'] = ['Arial', 'Microsoft YaHei', 'SimHei', 'DejaVu Sans'] plt.rcParams['axes.unicode_minus'] = False # 准备数据 plot_df = df[["Model", selected_plot_metric]].copy() plot_df = plot_df.dropna(subset=[selected_plot_metric]) if plot_df.empty: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, f"No data for metric '{metric_display_name}'", ha="center", va="center", fontsize=14) ax.axis("off") return fig # 重命名列以便使用 plot_df.columns = ['Model', 'Score'] # 确保Score是数值类型 plot_df['Score'] = pd.to_numeric(plot_df['Score'], errors='coerce') # 移除NaN值 plot_df = plot_df.dropna(subset=['Score']) if plot_df.empty: fig, ax = plt.subplots(figsize=(8, 6)) ax.text(0.5, 0.5, f"No valid data for metric '{metric_display_name}'", ha="center", va="center", fontsize=14) ax.axis("off") return fig # 保留2位小数 plot_df['Score'] = plot_df['Score'].round(2) # 根据排序模式排序 ascending = (plot_sort_mode != "Ascending (low → high)") ascending = plot_sort_mode.startswith("Descending") plot_df = plot_df.sort_values('Score', ascending=ascending) model_count = len(plot_df) figure_height = max(9, min(22, 0.5 * model_count + 4)) ytick_fontsize = max(12, min(25, 28 - model_count * 0.35)) value_fontsize = max(10, min(25, 26 - model_count * 0.28)) xlabel_fontsize = 24 if model_count > 20 else 28 ylabel_fontsize = 24 if model_count > 20 else 28 title_fontsize = 18 if model_count > 20 else 20 left_margin = 0.42 if model_count > 18 else 0.25 # 设置绘图风格 fig, ax = plt.subplots(figsize=(16, figure_height), dpi=100) colors = plt.get_cmap('coolwarm_r')(np.linspace(0.1, 0.9, len(plot_df))) # 绘制背景进度条 ax.barh(plot_df['Model'], [100]*len(plot_df), color="#FAFAFA", edgecolor='none', height=0.7) # 绘制真实的得分条 bars = ax.barh(plot_df['Model'], plot_df['Score'], color=colors, edgecolor='none', height=0.7) # 添加数值标签 (保留两位小数) for bar in bars: width = bar.get_width() ax.text(width + 1.5, bar.get_y() + bar.get_height()/2., f'{width:.2f}', ha='left', va='center', fontsize=value_fontsize, fontweight='bold', color='#444444') # 移除边框 for spine in ax.spines.values(): spine.set_visible(False) ax.tick_params(axis='both', which='both', length=0) # 细节美化 ax.set_xlabel(metric_display_name, fontsize=xlabel_fontsize, fontweight='bold', labelpad=5, x=0.32, horizontalalignment='center') plt.subplots_adjust(left=left_margin) ax.set_ylabel('Model', fontsize=ylabel_fontsize, labelpad=0, fontweight='bold') # 调整刻度字体 plt.yticks(fontsize=ytick_fontsize, fontweight='bold') ax.set_xticks([]) # 设置x轴范围,确保有足够空间显示标签 max_score = plot_df['Score'].max() ax.set_xlim(0, max(100, max_score * 1.2)) # 构建标题,包含筛选信息 # 只有当有筛选条件时才显示筛选信息 filter_parts = [] if model_filter and model_filter.strip(): filter_parts.append(f'Model: {model_filter}') if open_source_filter and open_source_filter != "All": filter_parts.append(f'Source: {open_source_filter}') if year_filter and year_filter != "All": filter_parts.append(f'Year: {year_filter}') if filter_parts: # 构建标题字符串 filter_str = f"[{', '.join(filter_parts)}]" total_length = len(f"{metric_display_name} Leaderboard {filter_str}") if total_length > 50: # 第一行:主标题和排序方式 first_line = f"{metric_display_name} Leaderboard" # 第二行:筛选条件 second_line = f"[{', '.join(filter_parts)}]" title = f"{first_line}\n{second_line}" else: title = f"{metric_display_name} Leaderboard [{', '.join(filter_parts)}]" else: title = f"{metric_display_name} Leaderboard" ax.set_title(title, fontsize=title_fontsize, fontweight='bold', pad=30, # 增加上边距 x=0.32, # 使用与x轴标签相同的x坐标 horizontalalignment='center', # 水平居中 y=1.05) # 稍微向上移动一点,避免与图表太近 # 调整整体布局 plt.tight_layout() return fig