CP Legendre commited on
Commit
344a63b
·
1 Parent(s): 744b1d0

Broaden categorical color palettes

Browse files
Files changed (3) hide show
  1. .gitignore +1 -0
  2. app.py +1 -1
  3. src/charts.py +72 -28
.gitignore CHANGED
@@ -6,6 +6,7 @@ __pycache__/
6
  *ipynb
7
  .vscode/
8
 
 
9
  eval-queue/
10
  eval-results/
11
  eval-queue-bk/
 
6
  *ipynb
7
  .vscode/
8
 
9
+ Backup/
10
  eval-queue/
11
  eval-results/
12
  eval-queue-bk/
app.py CHANGED
@@ -63,7 +63,7 @@ REPO_ID = "taagarwa/coding-agent-leaderboard"
63
  TOKEN = os.environ.get("HF_TOKEN")
64
  API = HfApi(token=TOKEN)
65
  COLOR_BY_CHOICES = ["Model", "Harness"]
66
- COLOR_PALETTE_CHOICES = ["Citrus", "Okabe-Ito", "High contrast"]
67
  DEFAULT_COLOR_PALETTE = "Citrus"
68
 
69
 
 
63
  TOKEN = os.environ.get("HF_TOKEN")
64
  API = HfApi(token=TOKEN)
65
  COLOR_BY_CHOICES = ["Model", "Harness"]
66
+ COLOR_PALETTE_CHOICES = ["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
67
  DEFAULT_COLOR_PALETTE = "Citrus"
68
 
69
 
src/charts.py CHANGED
@@ -9,45 +9,53 @@ import plotly.graph_objects as go
9
  from plotly.graph_objs._figure import Figure
10
 
11
  ColorBy = Literal["Model", "Harness"]
12
- PaletteName = Literal["Citrus", "Okabe-Ito", "High contrast"]
13
  DEFAULT_PALETTE: PaletteName = "Citrus"
14
 
15
- # Separate, colorblind-friendly palettes for each grouping dimension.
16
- # Models use warm citrus-adjacent colors; harnesses use cool complementary colors.
17
- # Keep the two palettes non-overlapping so switching "Color by" is visually obvious.
18
  MODEL_COLORS: dict[str, str] = {
19
  "GPT 5.5 - high": "#F59E0B", # amber
20
- "Opus 4.8": "#EAB308", # yellow
21
  "RedHatAI/Qwen3.6-35B-A3B-NVFP4": "#F97316", # orange
22
- "Sonnet 4.6": "#D97706", # dark amber
23
  }
24
 
25
  HARNESS_COLORS: dict[str, str] = {
26
  "Claude Code": "#06B6D4", # cyan
27
  "Codex": "#3B82F6", # blue
28
- "OpenCode": "#14B8A6", # teal
29
- "OpenClaw": "#8B5CF6", # violet
30
- "Pi": "#EC4899", # pink
31
- "Qwen Code": "#64748B", # slate
32
  "internal": "#94A3B8",
33
  }
34
 
35
  MODEL_FALLBACK_PALETTE = [
36
- "#F59E0B",
37
- "#EAB308",
38
- "#F97316",
39
- "#D97706",
40
- "#FACC15",
41
- "#FB923C",
 
 
 
 
42
  ]
43
 
44
  HARNESS_FALLBACK_PALETTE = [
45
- "#06B6D4",
46
- "#3B82F6",
47
- "#14B8A6",
48
- "#8B5CF6",
49
- "#EC4899",
50
- "#64748B",
 
 
 
 
51
  ]
52
 
53
  DARK_PAPER = "#15110F"
@@ -69,14 +77,22 @@ def clean_markdown_link(value: object) -> str:
69
 
70
  MODEL_PALETTES: dict[PaletteName, list[str]] = {
71
  "Citrus": MODEL_FALLBACK_PALETTE,
72
- "Okabe-Ito": ["#E69F00", "#F0E442", "#D55E00", "#CC79A7", "#A6761D", "#FB9A99"],
73
- "High contrast": ["#FFD166", "#FF9F1C", "#F77F00", "#F4D35E", "#EE964B", "#F95738"],
 
 
 
 
 
74
  }
75
 
76
  HARNESS_PALETTES: dict[PaletteName, list[str]] = {
77
  "Citrus": HARNESS_FALLBACK_PALETTE,
78
- "Okabe-Ito": ["#56B4E9", "#009E73", "#0072B2", "#CC79A7", "#999999", "#8DD3C7"],
79
- "High contrast": ["#4CC9F0", "#4895EF", "#80FFDB", "#B5179E", "#7209B7", "#ADB5BD"],
 
 
 
80
  }
81
 
82
 
@@ -103,14 +119,42 @@ def get_color(name: str, color_by: ColorBy, palette_name: str | None = DEFAULT_P
103
  return stable_color(name, color_by, palette_key)
104
 
105
 
 
 
 
 
 
 
106
  def color_map_for(
107
  values: pd.Series,
108
  color_by: ColorBy,
109
  palette_name: str | None = DEFAULT_PALETTE,
110
  ) -> dict[str, str]:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
111
  return {
112
- str(value): get_color(str(value), color_by, palette_name)
113
- for value in sorted(values.dropna().unique())
114
  }
115
 
116
 
 
9
  from plotly.graph_objs._figure import Figure
10
 
11
  ColorBy = Literal["Model", "Harness"]
12
+ PaletteName = Literal["Citrus", "Okabe-Ito", "High contrast", "Rainbow"]
13
  DEFAULT_PALETTE: PaletteName = "Citrus"
14
 
15
+ # Separate categorical palettes for each grouping dimension.
16
+ # Model and harness colors intentionally start from different hue families so
17
+ # switching "Color by" remains visually obvious.
18
  MODEL_COLORS: dict[str, str] = {
19
  "GPT 5.5 - high": "#F59E0B", # amber
20
+ "Opus 4.8": "#84CC16", # lime
21
  "RedHatAI/Qwen3.6-35B-A3B-NVFP4": "#F97316", # orange
22
+ "Sonnet 4.6": "#22C55E", # green
23
  }
24
 
25
  HARNESS_COLORS: dict[str, str] = {
26
  "Claude Code": "#06B6D4", # cyan
27
  "Codex": "#3B82F6", # blue
28
+ "OpenCode": "#8B5CF6", # violet
29
+ "OpenClaw": "#EC4899", # pink
30
+ "Pi": "#14B8A6", # teal
31
+ "Qwen Code": "#F43F5E", # rose
32
  "internal": "#94A3B8",
33
  }
34
 
35
  MODEL_FALLBACK_PALETTE = [
36
+ "#F59E0B", # amber
37
+ "#84CC16", # lime
38
+ "#F97316", # orange
39
+ "#22C55E", # green
40
+ "#EAB308", # yellow
41
+ "#FB7185", # rose
42
+ "#A3E635", # light lime
43
+ "#FACC15", # gold
44
+ "#F472B6", # pink
45
+ "#2DD4BF", # teal
46
  ]
47
 
48
  HARNESS_FALLBACK_PALETTE = [
49
+ "#06B6D4", # cyan
50
+ "#3B82F6", # blue
51
+ "#8B5CF6", # violet
52
+ "#EC4899", # pink
53
+ "#14B8A6", # teal
54
+ "#F43F5E", # rose
55
+ "#6366F1", # indigo
56
+ "#10B981", # emerald
57
+ "#A855F7", # purple
58
+ "#94A3B8", # slate
59
  ]
60
 
61
  DARK_PAPER = "#15110F"
 
77
 
78
  MODEL_PALETTES: dict[PaletteName, list[str]] = {
79
  "Citrus": MODEL_FALLBACK_PALETTE,
80
+ # Full Okabe-Ito palette. It is categorical and colorblind-friendly.
81
+ "Okabe-Ito": ["#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7", "#999999"],
82
+ # High-contrast colors are intentionally broad, not just orange/yellow variants.
83
+ "High contrast": ["#FFD166", "#06D6A0", "#118AB2", "#EF476F", "#A78BFA", "#F97316", "#22D3EE", "#E5E7EB"],
84
+ # Categorical rainbow-style palette. This is not a continuous colorscale; it is
85
+ # sampled as discrete colors so each category gets a distinct color.
86
+ "Rainbow": ["#E6194B", "#F58231", "#FFE119", "#3CB44B", "#42D4F4", "#4363D8", "#911EB4", "#F032E6", "#469990", "#9A6324"],
87
  }
88
 
89
  HARNESS_PALETTES: dict[PaletteName, list[str]] = {
90
  "Citrus": HARNESS_FALLBACK_PALETTE,
91
+ # Use the same broad Okabe-Ito family but with a different starting point so the
92
+ # harness mode does not visually mirror the model mode.
93
+ "Okabe-Ito": ["#0072B2", "#D55E00", "#CC79A7", "#009E73", "#56B4E9", "#E69F00", "#F0E442", "#999999"],
94
+ "High contrast": ["#38BDF8", "#34D399", "#A78BFA", "#F472B6", "#FACC15", "#FB923C", "#22D3EE", "#E5E7EB"],
95
+ "Rainbow": ["#4363D8", "#E6194B", "#3CB44B", "#F58231", "#911EB4", "#42D4F4", "#F032E6", "#FFE119", "#469990", "#9A6324"],
96
  }
97
 
98
 
 
119
  return stable_color(name, color_by, palette_key)
120
 
121
 
122
+ def palette_colors_for(color_by: ColorBy, palette_name: str | None = DEFAULT_PALETTE) -> list[str]:
123
+ palette_key = normalize_palette_name(palette_name)
124
+ palettes = MODEL_PALETTES if color_by == "Model" else HARNESS_PALETTES
125
+ return palettes[palette_key]
126
+
127
+
128
  def color_map_for(
129
  values: pd.Series,
130
  color_by: ColorBy,
131
  palette_name: str | None = DEFAULT_PALETTE,
132
  ) -> dict[str, str]:
133
+ unique_values = [str(value) for value in sorted(values.dropna().unique())]
134
+ palette_key = normalize_palette_name(palette_name)
135
+
136
+ # For the default Citrus palette, preserve hand-picked colors for known labels.
137
+ # Unknown labels still get sequential fallback colors to avoid hash collisions.
138
+ if palette_key == "Citrus":
139
+ named_colors = MODEL_COLORS if color_by == "Model" else HARNESS_COLORS
140
+ fallback_colors = palette_colors_for(color_by, palette_key)
141
+ color_map: dict[str, str] = {}
142
+ fallback_index = 0
143
+ for value in unique_values:
144
+ if value in named_colors:
145
+ color_map[value] = named_colors[value]
146
+ else:
147
+ color_map[value] = fallback_colors[fallback_index % len(fallback_colors)]
148
+ fallback_index += 1
149
+ return color_map
150
+
151
+ # Non-default palettes are assigned sequentially rather than by hash. Hashing can
152
+ # map multiple visible categories to the same color, which made the high-contrast
153
+ # harness palette look like only gray/blue/purple buckets.
154
+ palette = palette_colors_for(color_by, palette_key)
155
  return {
156
+ value: palette[index % len(palette)]
157
+ for index, value in enumerate(unique_values)
158
  }
159
 
160