| from __future__ import annotations |
|
|
| from typing import List |
|
|
| import matplotlib.pyplot as plt |
| import numpy as np |
| import pandas as pd |
| from matplotlib.lines import Line2D |
|
|
|
|
| class RadarPlotter: |
| def __init__(self, data_loader): |
| self.data_loader = data_loader |
| self.dimension_metrics = list(data_loader.dimension_metrics) |
| self.dimension_labels = list(data_loader.dimension_display_labels) |
| self.color_list = [ |
| "#1f77b4", |
| "#ff7f0e", |
| "#2ca02c", |
| "#d62728", |
| "#9467bd", |
| "#8c564b", |
| "#e377c2", |
| "#7f7f7f", |
| "#bcbd22", |
| "#17becf", |
| ] |
|
|
| def create_radar_chart(self, models_df: pd.DataFrame) -> plt.Figure: |
| if models_df.empty or not self.dimension_metrics: |
| fig, ax = plt.subplots(figsize=(8, 6)) |
| ax.text(0.5, 0.5, "No dimension metrics available for radar chart", ha="center", va="center", fontsize=14) |
| ax.axis("off") |
| return fig |
|
|
| plt.rcParams["font.family"] = "sans-serif" |
| plt.rcParams["font.sans-serif"] = ["Arial", "Microsoft YaHei", "SimHei", "DejaVu Sans"] |
| plt.rcParams["axes.unicode_minus"] = False |
|
|
| labels = self.dimension_labels |
| angles = np.linspace(0, 2 * np.pi, len(labels), endpoint=False).tolist() |
| angles += angles[:1] |
|
|
| fig = plt.figure(figsize=(13.5, 8.5)) |
| grid = fig.add_gridspec(1, 2, width_ratios=[3.2, 1.35], wspace=0.02) |
| ax = fig.add_subplot(grid[0, 0], polar=True) |
| ax_legend = fig.add_subplot(grid[0, 1]) |
| ax_legend.axis("off") |
| ax.set_theta_offset(np.pi / 2) |
| ax.set_theta_direction(-1) |
| ax.set_ylim(0, 105) |
| ax.set_xticks(angles[:-1]) |
| ax.set_xticklabels(labels, fontsize=11, fontweight="bold") |
| ax.set_yticks([20, 40, 60, 80, 100]) |
| ax.set_yticklabels(["20", "40", "60", "80", "100"], color="#666666") |
| ax.yaxis.grid(True, color="#D0D8E8", linewidth=0.8) |
| ax.xaxis.grid(True, color="#D0D8E8", linewidth=0.8) |
| ax.fill(np.linspace(0, 2 * np.pi, 400), [105] * 400, color="#EAF1FA", alpha=0.5) |
|
|
| model_scores = [] |
| for _, row in models_df.iterrows(): |
| values = [float(row.get(metric, 0) or 0) for metric in self.dimension_metrics] |
| valid_values = [value for value in values if value is not None] |
| avg_score = sum(valid_values) / len(valid_values) if valid_values else 0 |
| model_scores.append((row, avg_score)) |
|
|
| model_scores.sort(key=lambda item: item[1]) |
|
|
| legend_elements = [] |
| for index, (row, _) in enumerate(model_scores): |
| values = [float(row.get(metric, 0) or 0) for metric in self.dimension_metrics] |
| values += values[:1] |
| color = self.color_list[index % len(self.color_list)] |
| ax.plot(angles, values, color=color, linewidth=2.0, label=row["Model"]) |
| ax.fill(angles, values, color=color, alpha=0.06) |
| ax.scatter(angles[:-1], values[:-1], color=color, s=36, edgecolors="white", linewidths=0.5) |
| legend_elements.append( |
| Line2D( |
| [0], |
| [0], |
| color=color, |
| linestyle="-", |
| marker="o", |
| markersize=6, |
| linewidth=2.0, |
| markerfacecolor=color, |
| markeredgecolor="white", |
| markeredgewidth=0.5, |
| label=row["Model"], |
| ) |
| ) |
|
|
| if legend_elements: |
| ax_legend.legend( |
| handles=legend_elements, |
| loc="center left", |
| frameon=True, |
| fontsize=9, |
| fancybox=True, |
| shadow=False, |
| edgecolor="#d9d9d9", |
| facecolor="white", |
| borderpad=0.8, |
| labelspacing=0.55, |
| handlelength=2.0, |
| handletextpad=0.7, |
| ) |
|
|
| ax.set_title("Dimension Radar Chart", fontsize=18, fontweight="bold", pad=26) |
| fig.subplots_adjust(left=0.05, right=0.97, top=0.92, bottom=0.06) |
| return fig |
|
|