-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathplot_radar.py
More file actions
150 lines (125 loc) · 6.53 KB
/
plot_radar.py
File metadata and controls
150 lines (125 loc) · 6.53 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
from matplotlib.colors import LinearSegmentedColormap
from plot_lines import TECH_STYLE
def plot_radar_chart(data: dict, attributes: list, assigned_colors: dict = None, save_path: str = None, is_dark_mode: bool = True, font_size_axis: int = 12, font_size_legend: int = 12):
"""
Plot radar chart for AUP results with tech style.
Normalizes each attribute to [0, 1] based on the maximum value across methods.
Axes show the real values for each task.
data: {method_name: [val1, val2, ...]}
attributes: list of attribute names (e.g., datasets)
is_dark_mode: whether to use dark mode (True) or light mode (False)
font_size_axis: font size for axis labels
font_size_legend: font size for legend
"""
# Setup angles
N = len(attributes)
angles = np.linspace(0, 2 * np.pi, N, endpoint=False).tolist()
angles += [angles[0]] # Close the loop
# Normalize data independently per attribute
method_names = list(data.keys())
raw_matrix = np.array([data[m] for m in method_names]) # (num_methods, num_attributes)
# Calculate max for each attribute independently
attr_maxs = np.max(raw_matrix, axis=0) # (num_attributes,)
attr_maxs = np.array([np.ceil(m / 5.0) * 5.0 if m > 0 else 5.0 for m in attr_maxs])
# Normalize each attribute independently to [0, 1]
normalized_matrix = raw_matrix / attr_maxs[np.newaxis, :]
norm_data = {m: normalized_matrix[i] for i, m in enumerate(method_names)}
# Determine style settings
if is_dark_mode:
style_context = 'dark_background'
bg_color = 'black'
fg_color = 'white'
grid_color = 'white'
grid_alpha = 0.3
spine_color = 'white'
else:
style_context = 'default'
bg_color = '#F5F5F7' # Apple light gray
fg_color = 'black'
grid_color = '#86868b'
grid_alpha = 0.3
spine_color = '#86868b'
with plt.style.context(style_context):
fig, ax = plt.subplots(figsize=(12, 12), subplot_kw=dict(polar=True))
if not is_dark_mode:
fig.patch.set_facecolor(bg_color)
ax.set_facecolor(bg_color)
for i, (method_name, v) in enumerate(norm_data.items()):
# Determine color
if assigned_colors:
c_name = assigned_colors[i] if isinstance(assigned_colors, list) else assigned_colors.get(method_name, 'grey')
else:
c_name = 'grey'
grad_colors = TECH_STYLE.get(c_name, TECH_STYLE['grey'])
main_color = grad_colors[1]
cmap = LinearSegmentedColormap.from_list(f"tech_{c_name}", grad_colors)
# Prepare data for loop
v_loop = np.concatenate([v, [v[0]]])
# 1. Draw Gradient Line using LineCollection
points = np.array([angles, v_loop]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
norm = plt.Normalize(0, 2 * np.pi)
lc = LineCollection(segments, cmap=cmap, norm=norm)
lc.set_array(np.array(angles))
lc.set_linewidth(4) # Thicker lines
lc.set_alpha(0.9)
ax.add_collection(lc)
# 2. Fill area with low alpha
ax.fill(angles, v_loop, color=main_color, alpha=0.1)
# 3. Add markers
edge_c = main_color if is_dark_mode else 'white'
# Actually keep white edge on dark bg, and maybe main_color edge on light?
# Let's stick to white edge for contrast if filled with white, but here filled with nothing?
# Previous code: color='white', edgecolors=main_color
ax.scatter(angles, v_loop, color='white', edgecolors=main_color, s=100, zorder=10, lw=2)
# 4. Add to legend (proxy artist)
ax.plot([], [], color=main_color, label=method_name, linewidth=4)
# Styling
ax.set_theta_offset(np.pi / 2)
ax.set_theta_direction(-1)
# Grid and Spines
ax.grid(True, linestyle='--', alpha=grid_alpha, color=grid_color, linewidth=1)
ax.spines['polar'].set_visible(False)
# X-axis (Attributes) labels
ax.set_xticks(angles[:-1])
labels = ax.set_xticklabels(attributes, fontsize=font_size_axis, color=fg_color, fontweight='bold')
ax.tick_params(axis='x', pad=30)
# Special handling: move 'humaneval+' label right if 4 tasks
if len(attributes) == 4:
import matplotlib.transforms as mtransforms
for label in labels:
if label.get_text() == 'humaneval+':
# Shift the label to the right to prevent occlusion
offset = mtransforms.ScaledTranslation(40/72, 0, fig.dpi_scale_trans)
label.set_transform(label.get_transform() + offset)
# Y-axis settings
ax.set_yticklabels([]) # Hide default global ticks
ax.set_ylim(0, 1.05)
# Add custom ticks for each axis (Real values, independent scales)
grid_ticks = [0.2, 0.4, 0.6, 0.8, 1.0]
for i, angle in enumerate(angles[:-1]):
max_val = attr_maxs[i]
for t in grid_ticks:
val = t * max_val
if val.is_integer():
label = f"{int(val)}"
else:
label = f"{val:.1f}"
# Add text with background to improve readability
box_color = '#202020' if is_dark_mode else '#e0e0e0'
text_color_tick = 'white' if is_dark_mode else 'black'
ax.text(angle, t, label,
color=text_color_tick, fontsize=font_size_axis, fontweight='bold',
ha='center', va='center',
bbox=dict(facecolor=box_color, edgecolor='none', alpha=0.8, boxstyle='round,pad=0.2'))
# Legend and Title
plt.legend(loc='upper right', bbox_to_anchor=(1.1, 1.1), frameon=False, fontsize=font_size_legend, labelcolor=fg_color)
# plt.title('Multi-Task AUP Comparison (Normalized)', fontsize=font_size_axis, fontweight='bold', color=fg_color, pad=50)
if save_path:
fc = bg_color if not is_dark_mode else 'black'
plt.savefig(save_path, dpi=300, bbox_inches='tight', facecolor=fc)
print(f"Radar chart saved to {save_path}")
# plt.show()