This view is limited to 50 files because it contains too many changes. See the raw diff here.
Files changed (50) hide show
  1. .gitignore +0 -4
  2. AGENTS.md +0 -429
  3. CLAUDE.md +0 -28
  4. README.md +4 -31
  5. api.py +0 -592
  6. constants.py +1 -22
  7. data/full-imputed-cls/leaderboard.tex +53 -0
  8. data/full-imputed-cls/tabarena_leaderboard.csv +46 -0
  9. data/full-imputed-reg/leaderboard.tex +52 -0
  10. data/full-imputed-reg/tabarena_leaderboard.csv +45 -0
  11. data/{imputation_no/splits_all/tasks_multiclass/datasets_medium/tuning-impact-elo.png.zip → full-imputed-reg/time_plot.png.zip} +2 -2
  12. data/full-imputed/leaderboard.tex +53 -0
  13. data/full-imputed/tabarena_leaderboard.csv +46 -0
  14. data/imputation_no/splits_all/tasks_all/datasets_all/leaderboard_overview_explorer.html +0 -1411
  15. data/imputation_no/splits_all/tasks_all/datasets_all/leaderboard_table.html +0 -1162
  16. data/imputation_no/splits_all/tasks_all/datasets_all/n_datasets_51 +0 -0
  17. data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_explorer.html +0 -1230
  18. data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_improvability_vs_time_infer.png.zip +0 -3
  19. data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_points.csv +0 -63
  20. data/imputation_no/splits_all/tasks_all/datasets_all/pareto_n_configs_imp.png.zip +0 -3
  21. data/imputation_no/splits_all/tasks_all/datasets_all/tuning-impact-elo.png.zip +0 -3
  22. data/imputation_no/splits_all/tasks_all/datasets_all/tuning_trajectories.csv +0 -163
  23. data/imputation_no/splits_all/tasks_all/datasets_all/tuning_trajectories_explorer.html +0 -1230
  24. data/imputation_no/splits_all/tasks_all/datasets_all/website_leaderboard.csv +0 -71
  25. data/imputation_no/splits_all/tasks_all/datasets_all/winrate_explorer.html +0 -1169
  26. data/imputation_no/splits_all/tasks_all/datasets_all/winrate_matrix.csv +0 -29
  27. data/imputation_no/splits_all/tasks_all/datasets_all/winrate_matrix.png.zip +0 -3
  28. data/imputation_no/splits_all/tasks_all/datasets_medium/leaderboard_overview_explorer.html +0 -1411
  29. data/imputation_no/splits_all/tasks_all/datasets_medium/leaderboard_table.html +0 -1162
  30. data/imputation_no/splits_all/tasks_all/datasets_medium/n_datasets_15 +0 -0
  31. data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_explorer.html +0 -1230
  32. data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_improvability_vs_time_infer.png.zip +0 -3
  33. data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_points.csv +0 -63
  34. data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_n_configs_imp.png.zip +0 -3
  35. data/imputation_no/splits_all/tasks_all/datasets_medium/tuning-impact-elo.png.zip +0 -3
  36. data/imputation_no/splits_all/tasks_all/datasets_medium/tuning_trajectories.csv +0 -163
  37. data/imputation_no/splits_all/tasks_all/datasets_medium/tuning_trajectories_explorer.html +0 -1230
  38. data/imputation_no/splits_all/tasks_all/datasets_medium/website_leaderboard.csv +0 -71
  39. data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_explorer.html +0 -1169
  40. data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_matrix.csv +0 -29
  41. data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_matrix.png.zip +0 -3
  42. data/imputation_no/splits_all/tasks_all/datasets_small/leaderboard_overview_explorer.html +0 -1411
  43. data/imputation_no/splits_all/tasks_all/datasets_small/leaderboard_table.html +0 -1162
  44. data/imputation_no/splits_all/tasks_all/datasets_small/n_datasets_36 +0 -0
  45. data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_explorer.html +0 -1230
  46. data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_improvability_vs_time_infer.png.zip +0 -3
  47. data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_points.csv +0 -63
  48. data/imputation_no/splits_all/tasks_all/datasets_small/pareto_n_configs_imp.png.zip +0 -3
  49. data/imputation_no/splits_all/tasks_all/datasets_small/tuning-impact-elo.png.zip +0 -3
  50. data/imputation_no/splits_all/tasks_all/datasets_small/tuning_trajectories.csv +0 -163
.gitignore CHANGED
@@ -8,7 +8,3 @@ wheels/
8
 
9
  # Virtual environments
10
  .venv
11
-
12
- *.png
13
-
14
- .idea/
 
8
 
9
  # Virtual environments
10
  .venv
 
 
 
 
AGENTS.md DELETED
@@ -1,429 +0,0 @@
1
- # AGENTS.md
2
-
3
- Guidance for AI coding agents working in this repository.
4
-
5
- ## What this repo is
6
-
7
- Frontend code for the TabArena leaderboard, deployed as a Gradio app on a Hugging Face Space. The leaderboard ranks tabular ML models across tasks, splits, and dataset subsets.
8
-
9
- - Entrypoint: `main.py` (Gradio app plus the page's CSS/JS/head; HF Space runs it via the `app_file` declared in `README.md` frontmatter).
10
- - Pages and their layout: `pages.py` (one entry per leaderboard in `PAGES`); component builders: `views.py`.
11
- - Data paths and CSV parsing: `data_loading.py`. Agent-facing endpoints: `api.py`.
12
- - UI text and copy: `website_texts.py`.
13
- - Static constants (e.g. model-type emoji map): `constants.py`.
14
- - Precomputed artifacts (CSVs + zipped PNGs): `data/imputation_{no,yes}/splits_{...}/tasks_{...}/datasets_{...}/` for TabArena, `data_beyondarena/subsets/{subset}/` for BeyondArena.
15
- - Python: requires 3.12+. Gradio 6 (`sdk_version` in `README.md` must match the pin in `requirements.txt`).
16
-
17
- ## Local dev
18
-
19
- ```bash
20
- pip install -e ".[dev]" # or: uv pip install -r pyproject.toml
21
- python main.py # launches the Gradio app
22
- ```
23
-
24
- ## Data pipeline (not regenerated in this repo)
25
-
26
- Leaderboard data is produced upstream and dropped into `data/`:
27
-
28
- 1. Run `run_generate_website_artifacts.py` in the autogluon/tabarena repo.
29
- 2. Clear the local `data/` directory.
30
- 3. Unzip `clean_website_artifacts.zip` into `data/`.
31
-
32
- Do not edit files under `data/` by hand — they are generated artifacts.
33
-
34
- ## Architecture notes
35
-
36
- - A leaderboard view is one cell of a grid, described by the frozen `Subset` dataclass (`imputation`, `splits`, `tasks`, `datasets`) whose `rel_path` *is* the artifact directory. BeyondArena uses `BeyondSubset` with a single `subset` axis. The axis dicts in `data_loading.py` (`TASK_LABELS`, `DATASET_LABELS`, `BEYOND_SUBSET_LABELS`) define both the valid values and the tab order; the first entry of each is the default.
37
- - `LBContainer` (`data_loading.py`) loads one subset's `website_leaderboard.csv`, lazily unzips its PNGs, and inlines its interactive HTML plots.
38
- - The reactive render is driven by one `gr.State` per axis: selecting a tab or toggle updates that state, and the `@gr.render` block in `pages.py` rebuilds the subset's figures and table.
39
- - The full leaderboard table is `views.make_leaderboard`: a native `gr.Dataframe` (sorting, search, fullscreen, copy) inside one card with its filters, column picker and column key. Filtering is `views.filter_leaderboard`, kept pure so it can be tested without a Gradio context. It replaced the `gradio_leaderboard` custom component, which pinned Gradio < 6 — do not reintroduce that dependency.
40
-
41
- ## The agent-facing API (`api.py`)
42
-
43
- Hugging Face serves a generated `agents.md` for every Gradio Space at
44
- `huggingface.co/spaces/<id>/agents.md`, reachable from the **Agents** button on the Space page. It is not
45
- this file and cannot be overridden from the repo: it tells an agent to read `/gradio_api/info` and call
46
- what it finds. So `/gradio_api/info` is the real contract, and `api.py` owns it.
47
-
48
- - Four endpoints, registered by `register_api()` from inside the `gr.Blocks` context in `main.py`:
49
- `list_leaderboards`, `get_tabarena_leaderboard`, `get_beyondarena_leaderboard` and
50
- `get_pareto_frontier`. They read the same published CSVs the UI reads and reuse
51
- `data_loading.parse_model`, so the API cannot disagree with the table.
52
- - `get_pareto_frontier` is the one to point "which model should I use" at: the leaderboard's top row
53
- is often orders of magnitude slower than a model just behind it, so it returns the outright best,
54
- the non-dominated frontier on quality vs train/predict time (each entry with its speedup and
55
- quality gap), the best model inside a stated time budget, and a summary sentence to relay.
56
- - `kind` separates individual models from whole AutoML systems (rows the artifacts mark
57
- `Reference Pipeline`, i.e. AutoGluon). It defaults to `models`, because "the best tabular model"
58
- answered with an AutoML system is a wrong answer.
59
- - **Every UI event listener must pass `api_visibility="private"`.** Without it, each `.click` / `.change` /
60
- `.select` lambda lands in the public schema as `/lambda_7` with a parameter named `value_11`. Before this
61
- rule the Space advertised 23 such endpoints and nothing callable.
62
- - The same three functions are served as MCP tools at `/gradio_api/mcp/` (`launch(mcp_server=True)`,
63
- `gradio[mcp]` extra). Gradio derives each tool's schema from the type hints and its description from the
64
- docstring, so those are load-bearing for agents, not just for readers.
65
- - Record keys are the CSV headers with the sort-direction marker stripped and snake_cased
66
- (`Improvability (%) [⬇️]` becomes `improvability_pct`); `Model` is split into `model`, `variant` and
67
- `model_url`; `Verified` becomes a bool. `_api_key` in `api.py` is the single place that mapping lives.
68
- - Bulk readers should be pointed at the raw CSV instead, which `list_leaderboards` returns as a URL
69
- template. It serves identical numbers with no queue and no token.
70
-
71
- The generated `agents.md` tells agents to POST to `/gradio_api/call/v2/<endpoint>` with named parameters.
72
- That route only exists in Gradio 6, which is why the Space is pinned there; on 5.x it returned 405 and
73
- agents had to fall back to `/gradio_api/call/<endpoint>` with a positional `{"data": [...]}` body. Both
74
- work now.
75
-
76
- ## Conventions
77
-
78
- - Keep changes minimal — this is a small display app, not a framework. Avoid speculative abstraction.
79
- - Don't add comments that just describe what code does; the file is short enough to read.
80
- - User-facing copy lives in `website_texts.py`. Update it there, not inline.
81
- - Version bumps: edit `version` in `pyproject.toml` and the matching changelog entry in `website_texts.py` (`VERSION_HISTORY_BUTTON_TEXT`).
82
- - Prose you write, including user-facing copy in `website_texts.py`, follows [AI Writing Tropes to Avoid](#ai-writing-tropes-to-avoid) at the bottom of this file.
83
-
84
- ## Things to verify before reporting a UI change as done
85
-
86
- - Launch `python main.py` and click through the affected tabs in a browser.
87
- - Type checks and unit tests aren't meaningful here (none configured) — manual verification is the bar.
88
-
89
- ---
90
-
91
- # AI Writing Tropes to Avoid
92
-
93
- Applies to everything you write in this repo that a human reads: docstrings,
94
- comments, markdown docs, commit messages, PR descriptions, user-facing copy,
95
- and your replies in the chat.
96
-
97
- Source: [tropes.fyi](https://tropes.fyi) by [ossama.is](https://ossama.is)
98
-
99
- ---
100
-
101
- ## Word Choice
102
-
103
- ### "Quietly" and Other Magic Adverbs
104
-
105
- Overuse of "quietly" and similar adverbs to convey subtle importance or understated power. AI reaches for these adverbs to make mundane descriptions feel significant. Also includes: "deeply", "fundamentally", "remarkably", "arguably".
106
-
107
- **Avoid patterns like:**
108
- - "quietly orchestrating workflows, decisions, and interactions"
109
- - "the one that quietly suffocates everything else"
110
- - "a quiet intelligence behind it"
111
-
112
- ### "Delve" and Friends
113
-
114
- Used to be the most infamous AI tell. "Delve" went from an uncommon English word to appearing in a staggering percentage of AI-generated text. Part of a family of overused AI vocabulary including "certainly", "utilize", "leverage" (as a verb), "robust", "streamline", and "harness".
115
-
116
- **Avoid patterns like:**
117
- - "Let's delve into the details..."
118
- - "Delving deeper into this topic..."
119
- - "We certainly need to leverage these robust frameworks..."
120
-
121
- ### "Tapestry" and "Landscape"
122
-
123
- Overuse of ornate or grandiose nouns where simpler words would do. "Tapestry" is used to describe anything interconnected. "Landscape" is used to describe any field or domain. Other offenders: "paradigm", "synergy", "ecosystem", "framework".
124
-
125
- **Avoid patterns like:**
126
- - "The rich tapestry of human experience..."
127
- - "Navigating the complex landscape of modern AI..."
128
- - "The ever-evolving landscape of technology..."
129
-
130
- ### The "Serves As" Dodge
131
-
132
- Replacing simple "is" or "are" with pompous alternatives like "serves as", "stands as", "marks", or "represents". AI avoids basic copulas because its repetition penalty pushes it toward fancier constructions (I've studied this!).
133
-
134
- **Avoid patterns like:**
135
- - "The building serves as a reminder of the city's heritage."
136
- - "Gallery 825 serves as LAAA's exhibition space for contemporary art."
137
- - "The station marks a pivotal moment in the evolution of regional transit."
138
-
139
- ---
140
-
141
- ## Sentence Structure
142
-
143
- ### Negative Parallelism
144
-
145
- The "It's not X -- it's Y" pattern, often with an em dash. The single most commonly identified AI writing tell. Man I f*cking hate it. AI uses this to create false profundity by framing everything as a surprising reframe. One in a piece can be effective; ten in a blog post is a genuine insult to the reader. Before LLMs, people simply did not write like this at scale. Includes the causal variant "not because X, but because Y" where every explanation is framed as a surprise reveal, the em-dash dismissal "X -- not Y", and the cross-sentence reframe where the same noun is negated then repositioned: "The question isn't X. The question is Y."
146
-
147
- **Avoid patterns like:**
148
- - "It's not bold. It's backwards."
149
- - "Feeding isn't nutrition. It's dialysis."
150
- - "Half the bugs you chase aren't in your code. They're in your head."
151
-
152
- ### "Not X. Not Y. Just Z."
153
-
154
- The dramatic countdown pattern. AI builds tension by negating two or more things before revealing the actual point. Creates a false sense of narrowing down to the truth.
155
-
156
- **Avoid patterns like:**
157
- - "Not a bug. Not a feature. A fundamental design flaw."
158
- - "Not ten. Not fifty. Five hundred and twenty-three lint violations across 67 files."
159
- - "not recklessly, not completely, but enough"
160
-
161
- ### "The X? A Y."
162
-
163
- Self-posed rhetorical questions answered immediately in the next sentence or clause. The model asks a question nobody was asking, then answers it for dramatic effect. Thinks this is the epitome of great writing.
164
-
165
- **Avoid patterns like:**
166
- - "The result? Devastating."
167
- - "The worst part? Nobody saw it coming."
168
- - "The scary part? This attack vector is perfect for developers."
169
-
170
- ### Anaphora Abuse
171
-
172
- Repeating the same sentence opening multiple times in quick succession.
173
-
174
- **Avoid patterns like:**
175
- - "They assume that users will pay... They assume that developers will build... They assume that ecosystems will emerge... They assume that..."
176
- - "They could expose... They could offer... They could provide... They could create... They could let... They could unlock..."
177
- - "They have built engines, but not vehicles. They have built power, but not leverage. They have built walls, but not doors."
178
-
179
- ### Tricolon Abuse
180
-
181
- Overuse of the rule-of-three pattern, often extended to four or five. A single tricolon is elegant; three back-to-back tricolons are a pattern recognition failure.
182
-
183
- **Avoid patterns like:**
184
- - "Products impress people; platforms empower them. Products solve problems; platforms create worlds. Products scale linearly; platforms scale exponentially."
185
- - "identity, payments, compute, distribution"
186
- - "workflows, decisions, and interactions"
187
-
188
- ### "It's Worth Noting"
189
-
190
- Filler transitions that signal nothing. AI uses these phrases to introduce new points without actually connecting them to the previous argument. Also includes: "It bears mentioning", "Importantly", "Interestingly", "Notably".
191
-
192
- **Avoid patterns like:**
193
- - "It's worth noting that this approach has limitations."
194
- - "Importantly, we must consider the broader implications."
195
- - "Interestingly, this pattern repeats across industries."
196
-
197
- ### Superficial Analyses
198
-
199
- Tacking a present participle ("-ing") phrase onto the end of a sentence to inject shallow analysis that says nothing. The model attaches significance, legacy, or broader meaning to mundane facts using phrases like "highlighting its importance", "reflecting broader trends", or "contributing to the development of...".
200
-
201
- **Avoid patterns like:**
202
- - "contributing to the region's rich cultural heritage"
203
- - "This etymology highlights the enduring legacy of the community's resistance and the transformative power of unity in shaping its identity."
204
- - "underscoring its role as a dynamic hub of activity and culture"
205
-
206
- ### False Ranges
207
-
208
- Using "from X to Y" constructions where X and Y aren't on any real scale. In legitimate use, "from X to Y" implies a spectrum with a meaningful middle. AI uses it as a fancy way to list two loosely related things. "From innovation to cultural transformation" -- what's in between???? Nothing!
209
-
210
- **Avoid patterns like:**
211
- - "From innovation to implementation to cultural transformation."
212
- - "From the singularity of the Big Bang to the grand cosmic web."
213
- - "From problem-solving and tool-making to scientific discovery, artistic expression, and technological innovation."
214
-
215
- ---
216
-
217
- ## Paragraph Structure
218
-
219
- ### Short Punchy Fragments
220
-
221
- Excessive use of very short sentences or sentence fragments as standalone paragraphs for manufactured emphasis. RLHF training has pushed models toward "writing for readability" aimed at the lowest common denominator: one thought per sentence, no mental state-keeping required. It's an inhuman style. No real person writes first drafts this way because it doesn't match how humans think or speak.
222
-
223
- **Avoid patterns like:**
224
- - "He published this. Openly. In a book. As a priest."
225
- - "These weren't just products. And the software side matched. Then it professionalised. But I adapted."
226
- - "Platforms do."
227
-
228
- ### Listicle in a Trench Coat
229
-
230
- Numbered or labeled points dressed up as continuous prose. The model writes what is essentially a listicle but wraps each point in a paragraph that starts with "The first... The second... The third..." to disguise the format. Perhaps you told it to stop generating lists and it decided to do this instead... still very common.
231
-
232
- **Avoid patterns like:**
233
- - "The first wall is the absence of a free, scoped API... The second wall is the lack of delegated access... The third wall is the absence of scoped permissions..."
234
- - "The second takeaway is that... The third takeaway is that... The fourth takeaway is that..."
235
-
236
- ---
237
-
238
- ## Tone
239
-
240
- ### "Here's the Kicker"
241
-
242
- False suspense transitions that promise a revelation but deliver a point that did NOT need the buildup. The model uses these phrases to manufacture drama before an otherwise unremarkable observation LOL. Also includes: "Here's the thing", "Here's where it gets interesting", "Here's what most people miss", "Here's the starting point", "Here's the deal".
243
-
244
- **Avoid patterns like:**
245
- - "Here's the kicker."
246
- - "Here's the thing about AI adoption."
247
- - "Here's where it gets interesting."
248
-
249
- ### "Think of It As..."
250
-
251
- The patronizing analogy. AI constantly reaches for "Think of it as..." or "It's like a..." to simplify concepts. The model defaults to teacher mode and assumes the reader needs a metaphor to understand anything. Often produces analogies that are less clear than the original concept.
252
-
253
- **Avoid patterns like:**
254
- - "Think of it like a highway system for data."
255
- - "Think of it as a Swiss Army knife for your workflow."
256
- - "It's like asking someone to buy a car they're only allowed to sit in while it's parked."
257
-
258
- ### "Imagine a World Where..."
259
-
260
- The classic AI invitation to futurism. To sell the argument usually begins with "Imagine" followed by a list of wonderful things that will happen if the reader agrees with the premise.
261
-
262
- **Avoid patterns like:**
263
- - "Imagine a world where every tool you use -- your calendar, your inbox, your documents, your CRM, your code editor -- has a quiet intelligence behind it..."
264
- - "In that world, workflows stop being collections of manual steps and start becoming orchestrations."
265
-
266
- ### False Vulnerability
267
-
268
- Simulated self-awareness or honesty that reads as performative. The model pretends to break the fourth wall or admit a bias, creating a false sense of authenticity. Real vulnerability is specific and uncomfortable; AI vulnerability is polished and risk-free!!!!
269
-
270
- **Avoid patterns like:**
271
- - "And yes, I'm openly in love with the platform model"
272
- - "And yes, since we're being honest: I'm looking at you, OpenAI, Google, Anthropic, Meta"
273
- - "This is not a rant; it's a diagnosis"
274
-
275
- ### "The Truth Is Simple"
276
-
277
- Asserting that something is obvious, clear or simple instead of actually proving it. If you have to tell the reader your point is clear, it very likely isn't. Also includes the dramatic reveal variant: "but none of them is the real story. The real story is..." -- claiming privileged insight while waving away everything before it.
278
-
279
- **Avoid patterns like:**
280
- - "The reality is simpler and less flattering"
281
- - "History is unambiguous on this point"
282
- - "History is clear, the metrics are clear, the examples are clear"
283
-
284
- ### Grandiose Stakes Inflation
285
-
286
- Everything is the most important thing ever. AI inflates the stakes of every argument to world-historical significance. A blog post about API pricing becomes a meditation on the fate of civilization.
287
-
288
- **Avoid patterns like:**
289
- - "This will fundamentally reshape how we think about everything."
290
- - "will define the next era of computing"
291
- - "something entirely new"
292
-
293
- ### "Let's Break This Down"
294
-
295
- The pedagogical voice that assumes the reader needs hand-holding. AI defaults to a teacher-student dynamic even when writing for expert audiences. Also includes: "Let's unpack this", "Let's explore", "Let's dive in".
296
-
297
- **Avoid patterns like:**
298
- - "Let's break this down step by step."
299
- - "Let's unpack what this really means."
300
- - "Let's explore this idea further."
301
-
302
- ### Vague Attributions
303
-
304
- Attributing claims to unnamed authorities instead of being specific. AI loves to invoke "experts", "observers", "industry reports", and "several publications" without naming anyone. It also inflates the quantity of sources -- presenting what one person said as a widely held view, or writing "several publications have cited" when it means two. If you can't name the expert, you don't have a source.
305
-
306
- **Avoid patterns like:**
307
- - "Experts argue that this approach has significant drawbacks."
308
- - "Industry reports suggest that adoption is accelerating."
309
- - "Observers have cited the initiative as a turning point."
310
-
311
- ### Invented Concept Labels
312
-
313
- AI clusters invented compound labels that sound analytical without being grounded. It appends abstract problem-nouns (paradox, trap, creep, divide, vacuum, inversion) to domain words -- "supervision paradox", "acceleration trap", "workload creep" -- and uses them as if they're established, rigorously defined terms. They function as rhetorical shorthand: name a thing, skip the argument. Multiple such labels in the same piece is a strong signal of AI slop.
314
-
315
- **Avoid patterns like:**
316
- - "the supervision paradox"
317
- - "the acceleration trap"
318
- - "workload creep"
319
-
320
- ---
321
-
322
- ## Formatting
323
-
324
- ### Em-Dash Addiction
325
-
326
- Compulsive overuse of em dashes for dramatic pauses, parenthetical asides and pivot points. A human writer might use 2-3 per piece (and naturally); AI will use 20+.
327
-
328
- **Avoid patterns like:**
329
- - "The problem -- and this is the part nobody talks about -- is systemic."
330
- - "The tinkerer spirit didn't die of natural causes -- it was bought out."
331
- - "Not recklessly, not completely -- but enough -- enough to matter."
332
-
333
- ### Double-Hyphen Dash
334
-
335
- The em dash wearing a false moustache. Once "em dash means AI" became common knowledge, the character started getting swapped for a double hyphen: sometimes because the text passed through a markdown conversion, sometimes because someone ran a find-and-replace to look more human, sometimes because the model was steered off the character while keeping the habit. Either way the compulsive mid-sentence pivot survives the substitution, which is what actually gives it away. Writers who reach for double hyphens honestly do so once or twice out of typographic laziness, rarely fifteen times in one post. Flagged at five or more per thousand words.
336
-
337
- **Avoid patterns like:**
338
- - "The problem -- and this is the part nobody talks about -- is systemic."
339
- - "It's not a rewrite -- it's a reckoning."
340
- - "We shipped it fast -- maybe too fast -- and paid for it later."
341
-
342
- ### Bold-First Bullets
343
-
344
- Every bullet point or list item starts with a bolded phrase or sentence. Extremely common in Claude and ChatGPT markdown output. Almost nobody formats lists this way when writing by hand. It's a telltale sign of AI-generated documentation and blog posts AND README files (especially with emojis).
345
-
346
- **Avoid patterns like:**
347
- - "Every single bullet point begins with a bold keyword."
348
- - "**Security**: Environment-based configuration with..."
349
- - "**Performance**: Lazy loading of expensive resources..."
350
-
351
- ### Unicode Decoration
352
-
353
- Use of unicode arrows (->), smart/curly quotes, and other special characters that can't be easily typed on a standard keyboard. Real writers typing in a text editor produce straight quotes and -> or =>. Claude in particular loves the -> arrow.
354
-
355
- **Avoid patterns like:**
356
- - "Input → Processing → Output"
357
- - "This leads to better outcomes → which means higher engagement"
358
- - "“Smart quotes” instead of straight "quotes" that you’d actually type"
359
-
360
- ---
361
-
362
- ## Composition
363
-
364
- ### Fractal Summaries
365
-
366
- "What I'm going to tell you; what I'm telling you; what I just told you" -- applied at every level of the document. Every subsection gets a summary. Every section gets a summary. The document itself gets a summary.
367
-
368
- **Avoid patterns like:**
369
- - "In this section, we'll explore... [3000 words later] ...as we've seen in this section."
370
- - "A conclusion that restates every point already made in the previous 3000 words"
371
- - "And so we return to where we began."
372
-
373
- ### The Dead Metaphor
374
-
375
- Latching onto a single metaphor and beating it into the ground across the entire thing. A human writer would introduce a metaphor, use it then move on. AI will repeat the same metaphor 5-10 times.
376
-
377
- **Avoid patterns like:**
378
- - "The ecosystem needs ecosystems to build ecosystem value."
379
- - "Walls and doors used 30+ times in the same article"
380
- - "Every paragraph finds a way to say "primitives" again"
381
-
382
- ### Historical Analogy Stacking
383
-
384
- ESPECIALLY COMMON IN TECHNICAL WRITING: Rapid-fire listing of historical companies or tech revolutions to build false authority.
385
-
386
- **Avoid patterns like:**
387
- - "Apple didn't build Uber. Facebook didn't build Spotify. Stripe didn't build Shopify. AWS didn't build Airbnb."
388
- - "Every major technological shift -- the web, mobile, social, cloud -- followed the same pattern."
389
- - "Take Spotify... Or consider Uber... Airbnb followed a similar path... Shopify is another example... Even Discord..."
390
-
391
- ### One-Point Dilution
392
-
393
- Making a single argument and restating it in 10 different ways across thousands of words. The model pads a simple thesis to feel "comprehensive" by rephrasing the same idea with different metaphors, examples, and framings. An 800-word argument becomes 4000 words of circular repetition.
394
-
395
- **Avoid patterns like:**
396
- - "The same point, restated eight ways across 4000 words."
397
- - "Each section rephrases the thesis with a different metaphor but adds nothing new"
398
-
399
- ### Content Duplication
400
-
401
- Repeating entire sections or paragraphs verbatim within the same piece. This happens when the model loses track of what it has already written, especially in longer pieces. A dead giveaway of unedited AI output. Less common nowadays.
402
-
403
- **Avoid patterns like:**
404
- - "The same section appeared twice, word-for-word identical."
405
- - "Paragraph 3 and paragraph 17 are the same sentence reworded"
406
-
407
- ### The Signposted Conclusion
408
-
409
- Explicitly announcing the conclusion with "In conclusion", "To sum up", or "In summary". Competent writing doesn't need to tell you it's concluding. The reader can feel it. AI signals its structural moves because it's following a template, not writing organically.
410
-
411
- **Avoid patterns like:**
412
- - "In conclusion, the future of AI depends on..."
413
- - "To sum up, we've explored three key themes..."
414
- - "In summary, the evidence suggests..."
415
-
416
- ### "Despite Its Challenges..."
417
-
418
- The rigid formula where AI acknowledges problems only to immediately dismiss them. Always follows the same beat: "Despite its [positive words], [subject] faces challenges..." then ends with "Despite these challenges, [optimistic conclusion].".
419
-
420
- **Avoid patterns like:**
421
- - "Despite these challenges, the initiative continues to thrive."
422
- - "Despite its industrial and residential prosperity, Korattur faces challenges typical of urban areas."
423
- - "Despite their promising applications, pyroelectric materials face several challenges that must be addressed for broader adoption."
424
-
425
- ---
426
-
427
- Remember: any of these patterns used once might be fine. The problem is when
428
- multiple tropes appear together or when a single trope is used repeatedly.
429
- Write like a human: varied, imperfect, specific.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
CLAUDE.md DELETED
@@ -1,28 +0,0 @@
1
- # CLAUDE.md
2
-
3
- The primary agent guide for this repo is [`AGENTS.md`](./AGENTS.md). Read it
4
- first; everything in it applies here, including the writing-style rules in its
5
- "AI Writing Tropes to Avoid" section.
6
-
7
- This file only records Claude-specific notes.
8
-
9
- ## TL;DR
10
-
11
- * Repo: the Gradio frontend for the TabArena leaderboard, deployed as a
12
- Hugging Face Space. `main.py` is the entrypoint (`app_file` in the
13
- `README.md` frontmatter).
14
- * Copy lives in `website_texts.py`, constants in `constants.py`, generated
15
- artifacts in `data/`. Never hand-edit `data/`.
16
- * Verification bar is manual: run `python main.py` with this repo's own
17
- `.venv` and click through the affected tabs. No tests are configured.
18
- * Don't commit or push without an explicit ask. Pushing here deploys the
19
- Space, and `data/` is tracked via Git LFS + Xet.
20
-
21
- ## Refreshing the data
22
-
23
- `data/` is generated in the tabarena repo, not here. The `update-leaderboard`
24
- skill in `../tabarena/.claude/skills/` drives the whole flow: it generates the
25
- website artifacts, swaps them into this repo's `data/` (deleting the old
26
- subtree first, otherwise stale unzipped PNGs survive), and bumps the version
27
- history in `website_texts.py`. Invoke that skill from the tabarena repo rather
28
- than reproducing the steps by hand, and pass it the path to this repo.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -7,8 +7,8 @@ sdk: gradio
7
  app_file: main.py
8
  pinned: true
9
  license: apache-2.0
10
- short_description: 'Elo-ranked leaderboards for tabular ML, IID and beyond'
11
- sdk_version: 6.22.0
12
  ---
13
 
14
  # TabArena Leaderboard Code
@@ -18,38 +18,11 @@ The leaderboard is hosted on a HuggingFace space.
18
 
19
  Reference:
20
  * Website: https://tabarena.ai
21
- * Paper: https://tabarena.ai/paper-tabular-ml-iid-study
22
  * TabArena Codebase: https://tabarena.ai/code
23
 
24
  # Install LB Code for Development
25
 
26
  ```bash
27
  pip install -e ".[dev]"
28
- # Or
29
- uv pip install -r pyproject.toml
30
- ```
31
-
32
- # Reading the leaderboard programmatically
33
-
34
- The published numbers are plain CSVs in this repo, so the cheapest way to read
35
- them is a direct fetch (no queue, no token, `-L` because the raw path redirects
36
- to the resolve cache):
37
-
38
- ```bash
39
- curl -sL https://huggingface.co/spaces/TabArena/leaderboard/resolve/main/data/imputation_yes/splits_all/tasks_all/datasets_all/website_leaderboard.csv
40
- ```
41
-
42
- The path is `data/imputation_{yes,no}/splits_{all,lite}/tasks_{...}/datasets_{...}/`
43
- for TabArena and `data_beyondarena/subsets/{subset}/` for BeyondArena.
44
-
45
- The Space also serves three JSON endpoints for agents, defined in `api.py`:
46
- `list_leaderboards`, `get_tabarena_leaderboard`, `get_beyondarena_leaderboard`.
47
- Hugging Face advertises them through the **Agents** button on the Space page,
48
- which points at a generated `agents.md`. See AGENTS.md for the contract and its
49
- one caveat.
50
-
51
- # Current Steps to get results:
52
- 1. Run https://github.com/autogluon/tabarena/blob/main/scripts/run_generate_website_artifacts.py
53
- 2. Delete the current `data` folder contents in this repo to remove old results.
54
- 3. Unzip the generated `clean_website_artifacts.zip` zip file into the `data` folder in this repo.
55
- 4. Test the LB locally, otherwise you are done :)
 
7
  app_file: main.py
8
  pinned: true
9
  license: apache-2.0
10
+ short_description: 'TabArena'
11
+ sdk_version: 5.33.2
12
  ---
13
 
14
  # TabArena Leaderboard Code
 
18
 
19
  Reference:
20
  * Website: https://tabarena.ai
21
+ * Paper: TBA
22
  * TabArena Codebase: https://tabarena.ai/code
23
 
24
  # Install LB Code for Development
25
 
26
  ```bash
27
  pip install -e ".[dev]"
28
+ ```
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
api.py DELETED
@@ -1,592 +0,0 @@
1
- """The Space's machine-readable API, for agents rather than browsers.
2
-
3
- Hugging Face serves a generated ``agents.md`` for every Gradio Space
4
- (``huggingface.co/spaces/<id>/agents.md``). It is not a repo file and cannot be
5
- overridden: it tells an agent to read ``/gradio_api/info`` and call whatever it
6
- finds there. So the endpoints registered here *are* the agent-facing contract.
7
-
8
- Two rules keep that contract readable. Everything in this file is a named,
9
- described, typed endpoint; and every UI event listener elsewhere in the app
10
- passes ``api_visibility="private"``, so render callbacks stay out of the schema
11
- (a public listener would otherwise show up as ``/lambda_7`` with a parameter
12
- called ``value_11``).
13
-
14
- The same three functions are also served as MCP tools at ``/gradio_api/mcp/``,
15
- from these type hints and docstrings, because ``main.py`` launches with
16
- ``mcp_server=True``. Anything written here is read by an agent twice over.
17
-
18
- Records are the published CSVs with agent-friendly keys, not a second source of
19
- truth: the same files the site reads, the same model-cell parser the table uses.
20
- For bulk access, skip the queue and fetch the CSV directly (see
21
- :func:`list_leaderboards`, which hands out the URL template).
22
- """
23
-
24
- from __future__ import annotations
25
-
26
- import os
27
- import re
28
- from pathlib import Path
29
- from typing import Any, Literal, get_args
30
-
31
- import gradio as gr
32
- import pandas as pd
33
-
34
- from constants import Constants
35
- from data_loading import (
36
- BEYOND_DATA_DIR,
37
- BEYOND_SUBSET_LABELS,
38
- DATA_DIR,
39
- DATASET_LABELS,
40
- BeyondSubset,
41
- LBContainer,
42
- Subset,
43
- TASK_LABELS,
44
- parse_model,
45
- )
46
-
47
- # Axis values as Literals, so /gradio_api/info carries an enum per parameter.
48
- # That schema is the only place an agent reliably learns the valid values: a
49
- # rejected call comes back over the REST route as a bare `event: error` with a
50
- # null payload, message dropped. _validate_axes below keeps these in step with
51
- # the label dicts that define the data layout.
52
- # A leaderboard row is either a single model (evaluated in its default / tuned /
53
- # tuned+ensembled variants) or a whole AutoML system such as AutoGluon, which the
54
- # artifacts mark as a reference pipeline. Agents conflate the two otherwise, and
55
- # "the best tabular model" answered with an AutoML system is a wrong answer, so the
56
- # endpoints return models only unless asked for something else.
57
- KindAxis = Literal["models", "systems", "all"]
58
- SYSTEM_TYPE = Constants.reference
59
-
60
- # Which benchmark a cross-benchmark endpoint should read.
61
- BenchmarkAxis = Literal["tabarena", "beyondarena"]
62
- # What "best" means. The leaderboards rank by Elo; the rest are complementary.
63
- QualityAxis = Literal["elo", "score", "rank", "harmonic_rank", "improvability_pct"]
64
- _QUALITY_HIGHER_IS_BETTER = {
65
- "elo": True,
66
- "score": True,
67
- "rank": False,
68
- "harmonic_rank": False,
69
- "improvability_pct": False,
70
- }
71
- # What "cheap" means, and the record key each maps to.
72
- CostAxis = Literal["predict_time", "train_time"]
73
- _COST_KEY = {
74
- "predict_time": "median_predict_time_s_per_1k",
75
- "train_time": "median_train_time_s_per_1k",
76
- }
77
- _COST_UNITS = {
78
- "median_predict_time_s_per_1k": "seconds per 1000 rows to predict",
79
- "median_train_time_s_per_1k": "seconds per 1000 rows to train",
80
- }
81
-
82
- TasksAxis = Literal["all", "classification", "regression", "binary", "multiclass"]
83
- DatasetsAxis = Literal["all", "small", "medium"]
84
- ImputationAxis = Literal["yes", "no"]
85
- SplitsAxis = Literal["all", "lite"]
86
- BeyondSubsetAxis = Literal[
87
- "full",
88
- "random",
89
- "temporal",
90
- "grouped",
91
- "tiny",
92
- "small",
93
- "medium",
94
- "large",
95
- "low-dim",
96
- "high-dim",
97
- "text",
98
- "high-cardinality",
99
- ]
100
-
101
- IMPUTATION_VALUES = list(get_args(ImputationAxis))
102
- SPLITS_VALUES = list(get_args(SplitsAxis))
103
- KIND_VALUES = list(get_args(KindAxis))
104
-
105
-
106
- def _validate_axes() -> None:
107
- """Fail at import if an axis Literal has drifted from the data layout."""
108
- for name, literal, allowed in (
109
- ("TasksAxis", TasksAxis, TASK_LABELS),
110
- ("DatasetsAxis", DatasetsAxis, DATASET_LABELS),
111
- ("BeyondSubsetAxis", BeyondSubsetAxis, BEYOND_SUBSET_LABELS),
112
- ):
113
- if set(get_args(literal)) != set(allowed):
114
- raise RuntimeError(
115
- f"api.{name} is out of sync with data_loading: "
116
- f"{sorted(set(get_args(literal)) ^ set(allowed))} on one side only."
117
- )
118
-
119
-
120
- _validate_axes()
121
-
122
- # The Space serving this app; HF sets SPACE_ID in the container.
123
- SPACE_ID = os.environ.get("SPACE_ID", "TabArena/leaderboard")
124
- _RAW_URL = f"https://huggingface.co/spaces/{SPACE_ID}/resolve/main"
125
-
126
- # Column headers carry a sort-direction marker for the table widget; it is not
127
- # part of the data, so it is stripped before the header becomes a JSON key.
128
- _DIRECTION_RE = re.compile(r"\s*\[[⬆⬇]️?\]$")
129
- _KEY_OVERRIDES = {"#": "position", "TypeName": "type_name"}
130
-
131
- # Bulk-download URLs, templated on the same rel_path the app reads from so the
132
- # two cannot drift.
133
- _TABARENA_CSV_TEMPLATE = (
134
- f"{_RAW_URL}/{DATA_DIR.name}/"
135
- + Subset("{imputation}", "{splits}", "{tasks}", "{datasets}").rel_path
136
- + "/website_leaderboard.csv"
137
- )
138
- _BEYOND_CSV_TEMPLATE = (
139
- f"{_RAW_URL}/{BEYOND_DATA_DIR.name}/"
140
- + BeyondSubset("{subset}").rel_path
141
- + "/website_leaderboard.csv"
142
- )
143
-
144
-
145
- def _api_key(column: str) -> str:
146
- """Turn a CSV header into a JSON key: ``Improvability (%) [⬇️]`` -> ``improvability_pct``."""
147
- if column in _KEY_OVERRIDES:
148
- return _KEY_OVERRIDES[column]
149
- name = _DIRECTION_RE.sub("", column).replace("%", "pct").replace("/", "_per_")
150
- return re.sub(r"[^0-9a-zA-Z]+", "_", name).strip("_").lower()
151
-
152
-
153
- def _records(df: pd.DataFrame) -> list[dict[str, Any]]:
154
- """One JSON record per leaderboard row."""
155
- # NaN is not JSON; None is. Casting to object first keeps ints from turning
156
- # into floats on the way through.
157
- df = df.astype(object).where(pd.notna(df), None)
158
- records = []
159
- for row in df.to_dict("records"):
160
- record = {_api_key(key): value for key, value in row.items()}
161
- # The Model cell is markdown (`[TabFM (default)](url)`); agents want the
162
- # three parts separately.
163
- name, variant, url = parse_model(str(row["Model"]))
164
- record["model"] = name
165
- record["variant"] = variant
166
- record["model_url"] = url
167
- record["verified"] = str(row.get("Verified") or "").strip() == "✔️"
168
- record["kind"] = "system" if row.get("TypeName") == SYSTEM_TYPE else "model"
169
- records.append(record)
170
- return records
171
-
172
-
173
- def _select_kind(records: list[dict[str, Any]], kind: str) -> list[dict[str, Any]]:
174
- if kind == "all":
175
- return records
176
- wanted = "system" if kind == "systems" else "model"
177
- return [r for r in records if r["kind"] == wanted]
178
-
179
-
180
- def _check(axis: str, value: str, allowed: list[str]) -> None:
181
- if value not in allowed:
182
- raise gr.Error(f"Unknown {axis}={value!r}. Valid values: {', '.join(allowed)}.")
183
-
184
-
185
- def _pareto_frontier(
186
- records: list[dict[str, Any]], quality: str, cost_key: str
187
- ) -> list[dict[str, Any]]:
188
- """The non-dominated records: nothing is both better and cheaper.
189
-
190
- Walks cheapest-first and keeps a record only when it beats every cheaper one on
191
- quality, which is the 2-D skyline. Ties on cost keep the better-quality record.
192
- """
193
- higher_is_better = _QUALITY_HIGHER_IS_BETTER[quality]
194
- usable = [
195
- r
196
- for r in records
197
- if isinstance(r.get(quality), (int, float)) and isinstance(r.get(cost_key), (int, float))
198
- ]
199
- ordered = sorted(
200
- usable,
201
- key=lambda r: (r[cost_key], -r[quality] if higher_is_better else r[quality]),
202
- )
203
- frontier: list[dict[str, Any]] = []
204
- best = None
205
- for record in ordered:
206
- value = record[quality]
207
- if best is None or (value > best if higher_is_better else value < best):
208
- frontier.append(record)
209
- best = value
210
- return frontier
211
-
212
-
213
- def _best(records: list[dict[str, Any]], quality: str) -> dict[str, Any] | None:
214
- """The single best record on `quality`, ignoring rows that lack it."""
215
- usable = [r for r in records if isinstance(r.get(quality), (int, float))]
216
- if not usable:
217
- return None
218
- return max(
219
- usable,
220
- key=lambda r: r[quality] if _QUALITY_HIGHER_IS_BETTER[quality] else -r[quality],
221
- )
222
-
223
-
224
- def _brief(record: dict[str, Any] | None, quality: str, cost_key: str) -> dict[str, Any] | None:
225
- """The fields worth returning for one model in a Pareto answer."""
226
- if record is None:
227
- return None
228
- return {
229
- "model": record.get("model"),
230
- "variant": record.get("variant"),
231
- "kind": record.get("kind"),
232
- "type_name": record.get("type_name"),
233
- "verified": record.get("verified"),
234
- "model_url": record.get("model_url"),
235
- quality: record.get(quality),
236
- cost_key: record.get(cost_key),
237
- "median_train_time_s_per_1k": record.get("median_train_time_s_per_1k"),
238
- "median_predict_time_s_per_1k": record.get("median_predict_time_s_per_1k"),
239
- }
240
-
241
-
242
- def _describe(name: str | None, variant: str | None) -> str:
243
- return f"{name} ({variant})" if variant else str(name)
244
-
245
-
246
- def _load(data_root: Path, subset: Subset | BeyondSubset) -> list[dict[str, Any]]:
247
- lb = LBContainer(data_root=data_root, subset=subset, name="")
248
- try:
249
- return _records(lb.load_df())
250
- except FileNotFoundError:
251
- raise gr.Error(
252
- f"No results for {subset.rel_path!r} in this deployment. "
253
- "Call list_leaderboards for the subsets that are available."
254
- ) from None
255
-
256
-
257
- def get_tabarena_leaderboard(
258
- tasks: TasksAxis = "all",
259
- datasets: DatasetsAxis = "all",
260
- kind: KindAxis = "models",
261
- imputation: ImputationAxis = "yes",
262
- splits: SplitsAxis = "all",
263
- ) -> list[dict[str, Any]]:
264
- """Ranked TabArena results for one subset of the IID benchmark.
265
-
266
- Args:
267
- tasks: Task subset, one of ``all``, ``classification``, ``regression``,
268
- ``binary``, ``multiclass``.
269
- datasets: Dataset-size subset, one of ``all``, ``small``, ``medium``.
270
- kind: What to rank. ``models`` (the default) returns individual models
271
- such as TabPFN, RealMLP or LightGBM. ``systems`` returns whole AutoML
272
- systems such as AutoGluon, which tune and ensemble many models
273
- internally and are therefore not comparable to a single model.
274
- ``all`` returns both, as the website's table does. Ask for ``models``
275
- when the question is "which model should I use" and ``systems`` when
276
- it is "which AutoML framework should I use"; every record also
277
- carries its own ``kind``.
278
- imputation: ``yes`` includes models that cannot run on every dataset,
279
- imputing the missing runs with a default RandomForest (this is what
280
- the website shows by default); ``no`` drops them.
281
- splits: ``all`` for the full repeated-CV protocol, ``lite`` for the
282
- single-split TabArena-Lite results.
283
-
284
- Returns:
285
- One record per model variant, best first, with ``model``, ``variant``,
286
- ``elo``, ``score``, ``rank``, ``median_train_time_s_per_1k`` and the
287
- remaining leaderboard columns. ``variant`` is ``default``, ``tuned`` or
288
- ``tuned + ensembled``, and empty for reference pipelines such as
289
- AutoGluon, which are whole pipelines rather than one model's variant
290
- (``type_name`` identifies those). Note the website's variant filter
291
- groups them with the tuned ensembles instead.
292
- """
293
- _check("tasks", tasks, list(TASK_LABELS))
294
- _check("datasets", datasets, list(DATASET_LABELS))
295
- _check("kind", kind, KIND_VALUES)
296
- _check("imputation", imputation, IMPUTATION_VALUES)
297
- _check("splits", splits, SPLITS_VALUES)
298
- subset = Subset(imputation=imputation, splits=splits, tasks=tasks, datasets=datasets)
299
- return _select_kind(_load(DATA_DIR, subset), kind)
300
-
301
-
302
- def get_beyondarena_leaderboard(
303
- subset: BeyondSubsetAxis = "full", kind: KindAxis = "models"
304
- ) -> list[dict[str, Any]]:
305
- """Ranked BeyondArena results for one subset, on the recommended core protocol.
306
-
307
- BeyondArena goes beyond the IID assumption: it spans random, temporal and
308
- grouped splits. It has a single subset axis rather than TabArena's grid.
309
-
310
- Args:
311
- subset: One of ``full``, ``random``, ``temporal``, ``grouped``, ``tiny``,
312
- ``small``, ``medium``, ``large``, ``low-dim``, ``high-dim``, ``text``,
313
- ``high-cardinality``.
314
- kind: ``models`` (the default), ``systems`` for whole AutoML systems, or
315
- ``all``. See :func:`get_tabarena_leaderboard` for the distinction.
316
-
317
- Returns:
318
- One record per model variant, best first, in the same shape as
319
- :func:`get_tabarena_leaderboard`.
320
- """
321
- _check("subset", subset, list(BEYOND_SUBSET_LABELS))
322
- _check("kind", kind, KIND_VALUES)
323
- return _select_kind(_load(BEYOND_DATA_DIR, BeyondSubset(subset=subset)), kind)
324
-
325
-
326
- def get_pareto_frontier(
327
- benchmark: BenchmarkAxis = "tabarena",
328
- quality: QualityAxis = "elo",
329
- cost: CostAxis = "predict_time",
330
- kind: KindAxis = "models",
331
- tasks: TasksAxis = "all",
332
- datasets: DatasetsAxis = "all",
333
- beyond_subset: BeyondSubsetAxis = "full",
334
- max_train_time_s_per_1k: float = 0.0,
335
- max_predict_time_s_per_1k: float = 0.0,
336
- imputation: ImputationAxis = "yes",
337
- splits: SplitsAxis = "all",
338
- ) -> dict[str, Any]:
339
- """The accuracy-versus-time trade-off, for answering "which model should I use".
340
-
341
- The top of a leaderboard is only one answer, and often the wrong one: the
342
- highest-Elo model can be orders of magnitude slower than one a hair behind it.
343
- This returns the whole trade-off instead — the outright best, the models that
344
- nothing beats on both axes at once, and the best model that fits a time budget.
345
-
346
- Args:
347
- benchmark: ``tabarena`` (IID) or ``beyondarena`` (temporal / grouped splits).
348
- quality: What "better" means: ``elo`` (default) or ``score``, where higher
349
- wins, or ``rank`` / ``harmonic_rank`` / ``improvability_pct``, where
350
- lower wins.
351
- cost: What "cheaper" means: ``predict_time`` (default, what matters for
352
- serving) or ``train_time`` (what matters for retraining).
353
- kind: ``models`` (default), ``systems``, or ``all``. See
354
- :func:`get_tabarena_leaderboard`.
355
- tasks: TabArena task subset; ignored when `benchmark` is ``beyondarena``.
356
- datasets: TabArena dataset-size subset; ignored for ``beyondarena``.
357
- beyond_subset: BeyondArena subset; ignored when `benchmark` is ``tabarena``.
358
- max_train_time_s_per_1k: Optional ceiling on median train seconds per 1000
359
- rows. 0 means no limit.
360
- max_predict_time_s_per_1k: Optional ceiling on median predict seconds per
361
- 1000 rows. 0 means no limit. Use this for "what is the best model I can
362
- afford to serve".
363
- imputation: TabArena only; see :func:`get_tabarena_leaderboard`.
364
- splits: TabArena only; see :func:`get_tabarena_leaderboard`.
365
-
366
- Returns:
367
- ``best_overall`` (the outright leader, ignoring cost), ``frontier`` (the
368
- non-dominated models, cheapest first, each with ``speedup_vs_best`` and
369
- ``quality_gap_vs_best``), ``best_within_limits`` (the leader among models
370
- satisfying the time ceilings, absent when no ceiling was given),
371
- ``dominated_count``, and a ``summary`` sentence to relay.
372
- """
373
- _check("benchmark", benchmark, list(get_args(BenchmarkAxis)))
374
- _check("quality", quality, list(_QUALITY_HIGHER_IS_BETTER))
375
- _check("cost", cost, list(_COST_KEY))
376
- _check("kind", kind, KIND_VALUES)
377
- if benchmark == "beyondarena":
378
- records = get_beyondarena_leaderboard(subset=beyond_subset, kind=kind)
379
- where = f"BeyondArena ({beyond_subset}, core protocol)"
380
- else:
381
- records = get_tabarena_leaderboard(
382
- tasks=tasks, datasets=datasets, kind=kind, imputation=imputation, splits=splits
383
- )
384
- where = f"TabArena ({tasks} tasks, {datasets} datasets)"
385
-
386
- cost_key = _COST_KEY[cost]
387
- best_overall = _best(records, quality)
388
- frontier = _pareto_frontier(records, quality, cost_key)
389
-
390
- limits = {
391
- "median_train_time_s_per_1k": max_train_time_s_per_1k,
392
- "median_predict_time_s_per_1k": max_predict_time_s_per_1k,
393
- }
394
- active_limits = {key: value for key, value in limits.items() if value and value > 0}
395
- within = records
396
- for key, ceiling in active_limits.items():
397
- within = [
398
- r for r in within if isinstance(r.get(key), (int, float)) and r[key] <= ceiling
399
- ]
400
- best_within = _best(within, quality) if active_limits else None
401
-
402
- entries = []
403
- for record in frontier:
404
- entry = _brief(record, quality, cost_key)
405
- if best_overall and isinstance(best_overall.get(cost_key), (int, float)):
406
- reference = best_overall[cost_key]
407
- entry["speedup_vs_best"] = (
408
- round(reference / record[cost_key], 2) if record[cost_key] else None
409
- )
410
- entry["quality_gap_vs_best"] = round(
411
- abs(best_overall[quality] - record[quality]), 4
412
- )
413
- entries.append(entry)
414
-
415
- return {
416
- "benchmark": benchmark,
417
- "subset": where,
418
- "quality": quality,
419
- "higher_is_better": _QUALITY_HIGHER_IS_BETTER[quality],
420
- "cost": cost_key,
421
- "kind": kind,
422
- "best_overall": _brief(best_overall, quality, cost_key),
423
- "frontier": entries,
424
- "dominated_count": max(0, len(records) - len(frontier)),
425
- "limits": active_limits or None,
426
- "best_within_limits": _brief(best_within, quality, cost_key),
427
- "summary": _pareto_summary(
428
- where, quality, cost_key, best_overall, entries, active_limits, best_within
429
- ),
430
- }
431
-
432
-
433
- def _pareto_summary(
434
- where: str,
435
- quality: str,
436
- cost_key: str,
437
- best_overall: dict[str, Any] | None,
438
- frontier: list[dict[str, Any]],
439
- limits: dict[str, float],
440
- best_within: dict[str, Any] | None,
441
- ) -> str:
442
- """One paragraph an agent can relay instead of reciting the whole frontier."""
443
- if best_overall is None:
444
- return f"No {quality} values are available for {where}."
445
- lead = _describe(best_overall.get("model"), best_overall.get("variant"))
446
- units = _COST_UNITS.get(cost_key, cost_key)
447
- parts = [
448
- f"On {where}, the best {quality} is {lead} at {best_overall[quality]} "
449
- f"({best_overall.get(cost_key)} {units})."
450
- ]
451
- if len(frontier) > 1:
452
- cheapest = frontier[0]
453
- parts.append(
454
- f"{len(frontier)} models are on the accuracy/time frontier. The cheapest, "
455
- f"{_describe(cheapest.get('model'), cheapest.get('variant'))}, is "
456
- f"{cheapest.get('speedup_vs_best')}x faster but {cheapest.get('quality_gap_vs_best')} "
457
- f"{quality} behind."
458
- )
459
- # The trade-off worth naming is the smallest sacrifice that still buys a real
460
- # speedup, not the biggest speedup (that is just the cheapest model again).
461
- bargains = [
462
- entry
463
- for entry in frontier
464
- if (entry.get("speedup_vs_best") or 0) >= 2 and entry.get("quality_gap_vs_best")
465
- ]
466
- if bargains:
467
- pick = min(bargains, key=lambda e: e["quality_gap_vs_best"])
468
- parts.append(
469
- # "at a cost of" rather than "less", which reads backwards for the
470
- # metrics where lower is better.
471
- f"The best compromise is {_describe(pick.get('model'), pick.get('variant'))}: "
472
- f"{pick.get('speedup_vs_best')}x faster at a cost of "
473
- f"{pick.get('quality_gap_vs_best')} {quality}."
474
- )
475
- if limits:
476
- stated = ", ".join(f"{key} <= {value}" for key, value in limits.items())
477
- if best_within:
478
- parts.append(
479
- f"Within {stated}, the best is "
480
- f"{_describe(best_within.get('model'), best_within.get('variant'))} at "
481
- f"{best_within[quality]}."
482
- )
483
- else:
484
- parts.append(f"No model satisfies {stated}.")
485
- return " ".join(parts)
486
-
487
-
488
- def list_leaderboards() -> dict[str, Any]:
489
- """Describe the available leaderboards, their subset axes, and the bulk-download URLs.
490
-
491
- Call this first: it lists the valid argument values for the other endpoints
492
- and the record keys they return. It also gives the raw CSV URL template for
493
- each benchmark, which serves the identical numbers over plain HTTP with no
494
- queue or session, and is the better choice for reading many subsets.
495
- """
496
- default_rows = _load(DATA_DIR, Subset())
497
- return {
498
- "leaderboards": [
499
- {
500
- "name": "tabarena",
501
- "endpoint": "/get_tabarena_leaderboard",
502
- "description": "Tabular ML on 51 curated IID datasets, ranked by Elo.",
503
- "axes": {
504
- "tasks": list(TASK_LABELS),
505
- "datasets": list(DATASET_LABELS),
506
- "kind": KIND_VALUES,
507
- "imputation": IMPUTATION_VALUES,
508
- "splits": SPLITS_VALUES,
509
- },
510
- "csv_url_template": _TABARENA_CSV_TEMPLATE,
511
- },
512
- {
513
- "name": "beyondarena",
514
- "endpoint": "/get_beyondarena_leaderboard",
515
- "description": (
516
- "Tabular ML beyond the IID assumption: random, temporal and grouped "
517
- "splits across dataset sizes and feature types."
518
- ),
519
- "axes": {"subset": list(BEYOND_SUBSET_LABELS), "kind": KIND_VALUES},
520
- "csv_url_template": _BEYOND_CSV_TEMPLATE,
521
- },
522
- ],
523
- "record_keys": sorted(default_rows[0]) if default_rows else [],
524
- "kinds": {
525
- "models": "Individual models (TabPFN, RealMLP, LightGBM, ...). The default.",
526
- "systems": (
527
- f"Whole AutoML systems (AutoGluon, ...), marked '{SYSTEM_TYPE}' in the "
528
- "artifacts. They tune and ensemble many models internally, so ranking them "
529
- "against a single model compares different things."
530
- ),
531
- "all": "Both, as the website's table shows them.",
532
- },
533
- "choosing_a_model": (
534
- "For 'which model should I use', call get_pareto_frontier rather than reading the "
535
- "top row here: it returns the accuracy/time trade-off, since the highest-Elo model "
536
- "is often orders of magnitude slower than one just behind it. It also takes "
537
- "train- and predict-time budgets."
538
- ),
539
- "notes": (
540
- "Scores are read from the published artifacts, so they match the website exactly. "
541
- "Higher is better for elo and score; lower is better for rank, harmonic_rank, "
542
- "improvability_pct and the time columns. Every record carries a `kind` of "
543
- "'model' or 'system'; the get_* endpoints return models only unless asked."
544
- ),
545
- }
546
-
547
-
548
- def register_api() -> None:
549
- """Register the endpoints. Call inside the app's ``gr.Blocks`` context."""
550
- gr.api(
551
- list_leaderboards,
552
- api_name="list_leaderboards",
553
- api_description=(
554
- "List the available TabArena leaderboards, the valid subset values for each, "
555
- "the difference between models and systems, the keys every record carries, and "
556
- "the raw CSV URLs for bulk download. Call this before the get_* endpoints."
557
- ),
558
- )
559
- gr.api(
560
- get_tabarena_leaderboard,
561
- api_name="get_tabarena_leaderboard",
562
- api_description=(
563
- "Ranked TabArena results (Elo, score, ranks, train/predict time) for one subset "
564
- "of the IID benchmark, selected by tasks, datasets, imputation and splits. "
565
- "Pass kind='models' (the default) for individual models such as TabPFN or "
566
- "LightGBM, kind='systems' for whole AutoML systems such as AutoGluon, or "
567
- "kind='all' for both. A question about the best model wants 'models'; a "
568
- "question about the best AutoML framework wants 'systems'."
569
- ),
570
- )
571
- gr.api(
572
- get_beyondarena_leaderboard,
573
- api_name="get_beyondarena_leaderboard",
574
- api_description=(
575
- "Ranked BeyondArena results for one subset (random, temporal, grouped splits; "
576
- "size and feature-type buckets), on the recommended core protocol. Takes the "
577
- "same kind='models' | 'systems' | 'all' distinction as the TabArena endpoint."
578
- ),
579
- )
580
- gr.api(
581
- get_pareto_frontier,
582
- api_name="get_pareto_frontier",
583
- api_description=(
584
- "The accuracy-versus-time trade-off for one subset: the outright best model, "
585
- "the models nothing beats on both accuracy and speed at once (each with its "
586
- "speedup and accuracy gap versus the leader), and the best model that fits a "
587
- "train- or predict-time budget. Prefer this over get_*_leaderboard for "
588
- "'which model should I use', 'what is the best model', or any question about "
589
- "models that are faster to fit or to serve: the top of the leaderboard is "
590
- "often far slower than a model just behind it. Returns a summary sentence."
591
- ),
592
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
constants.py CHANGED
@@ -8,33 +8,12 @@ class Constants:
8
  # Not Used
9
  other: str = "Other"
10
 
11
- # Order here defines the display order of the type legend and filters.
12
  model_type_emoji = {
 
13
  Constants.foundational: "🧠⚡",
14
  Constants.neural_network:"🧠🔁",
15
- Constants.tree: "🌳",
16
  Constants.baseline: "📏",
17
  # Not used
18
  Constants.other: "❓",
19
  Constants.reference:"📊",
20
  }
21
-
22
- # Per-variant accent color, taken from the interactive Leaderboard Overview
23
- # explorer's --var-* (dark theme) so its variant toggles and the leaderboard
24
- # table's agree. Keys must match views.VARIANT_VALUES.
25
- variant_color = {
26
- "default": "#4386d5",
27
- "tuned": "#c05f38",
28
- "tuned + ensembled": "#289972",
29
- }
30
-
31
- # Per-type accent color (readable as text/border on the dark theme). Used for the
32
- # type legend and to color-code model names in the cross-subset overview.
33
- model_type_color = {
34
- Constants.foundational: "#b07cf0", # purple
35
- Constants.neural_network: "#5aa9e6", # blue
36
- Constants.tree: "#5cb85c", # green
37
- Constants.baseline: "#9e9e9e", # gray
38
- Constants.other: "#9e9e9e", # gray
39
- Constants.reference: "#f0a35a", # orange
40
- }
 
8
  # Not Used
9
  other: str = "Other"
10
 
 
11
  model_type_emoji = {
12
+ Constants.tree: "🌳",
13
  Constants.foundational: "🧠⚡",
14
  Constants.neural_network:"🧠🔁",
 
15
  Constants.baseline: "📏",
16
  # Not used
17
  Constants.other: "❓",
18
  Constants.reference:"📊",
19
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/full-imputed-cls/leaderboard.tex ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{tabular}{llcccccrr}
2
+ \toprule
3
+ \textbf{Model} & \textbf{Elo ($\uparrow$)} & \textbf{Norm.} & \textbf{Avg.} & \textbf{Harm.} & \textbf{\#wins ($\uparrow$)} & \textbf{Improva-} & \textbf{Train time} & \textbf{Predict time} \\
4
+ & & \textbf{score ($\uparrow$)} & \textbf{rank ($\downarrow$)} & \textbf{mean} & & \textbf{bility ($\downarrow$)} & \textbf{per 1K [s]} & \textbf{per 1K [s]} \\
5
+ & & & & \textbf{rank ($\downarrow$)} & & & & \\
6
+ \midrule
7
+ TabM (T+E) & \textcolor{gold}{\textbf{1574${}_{-28,+28}$}} & \textcolor{bronze}{\textbf{0.505}} & \textcolor{gold}{\textbf{9.6}} & 4.5 & 3 & \textcolor{bronze}{\textbf{8.2\%}} & 2466.21 & 1.50 \\
8
+ AutoGluon 1.3 (4h) & \textcolor{silver}{\textbf{1573${}_{-34,+30}$}} & \textcolor{gold}{\textbf{0.577}} & \textcolor{silver}{\textbf{9.7}} & \textcolor{silver}{\textbf{3.3}} & \textcolor{silver}{\textbf{6}} & \textcolor{gold}{\textbf{6.9\%}} & 1322.72 & 2.36 \\
9
+ RealMLP (T+E) & \textcolor{bronze}{\textbf{1552${}_{-30,+32}$}} & 0.470 & \textcolor{bronze}{\textbf{10.5}} & 7.4 & 0 & \textcolor{bronze}{\textbf{8.2\%}} & 6519.69 & 10.84 \\
10
+ LightGBM (T+E) & 1537${}_{-30,+26}$ & 0.421 & 11.1 & 6.4 & 1 & 9.8\% & 382.05 & 1.49 \\
11
+ TabICL (D) & 1530${}_{-31,+23}$ & \textcolor{silver}{\textbf{0.512}} & 11.4 & \textcolor{bronze}{\textbf{3.5}} & \textcolor{silver}{\textbf{6}} & \textcolor{silver}{\textbf{7.9\%}} & 8.68 & 1.74 \\
12
+ TabM (T) & 1487${}_{-30,+29}$ & 0.420 & 13.2 & 6.4 & 1 & 9.3\% & 2466.21 & 0.18 \\
13
+ CatBoost (T+E) & 1479${}_{-26,+24}$ & 0.395 & 13.6 & 8.7 & 0 & 9.1\% & 1372.94 & 0.56 \\
14
+ CatBoost (T) & 1469${}_{-27,+25}$ & 0.379 & 13.9 & 7.0 & 1 & 9.3\% & 1372.94 & 0.07 \\
15
+ LightGBM (T) & 1463${}_{-29,+30}$ & 0.336 & 14.3 & 12.1 & 0 & 10.6\% & 382.05 & 0.25 \\
16
+ CatBoost (D) & 1454${}_{-28,+23}$ & 0.362 & 14.7 & 7.1 & 1 & 10.3\% & 5.72 & 0.08 \\
17
+ TabPFNv2 (T+E) & 1454${}_{-31,+25}$ & 0.497 & 14.7 & \textcolor{gold}{\textbf{3.1}} & \textcolor{gold}{\textbf{8}} & 9.7\% & 3008.22 & 20.85 \\
18
+ XGBoost (T+E) & 1452${}_{-28,+31}$ & 0.332 & 14.8 & 9.3 & 0 & 10.7\% & 685.87 & 1.45 \\
19
+ ModernNCA (T) & 1416${}_{-34,+24}$ & 0.289 & 16.5 & 9.7 & 1 & 10.5\% & 4879.89 & 0.52 \\
20
+ XGBoost (T) & 1412${}_{-26,+28}$ & 0.279 & 16.6 & 13.3 & 0 & 11.1\% & 685.87 & 0.21 \\
21
+ ModernNCA (T+E) & 1410${}_{-28,+27}$ & 0.382 & 16.7 & 7.5 & 0 & 10.4\% & 4879.89 & 8.74 \\
22
+ TabPFNv2 (T) & 1388${}_{-30,+33}$ & 0.385 & 17.8 & 5.2 & 1 & 12.1\% & 3008.22 & 0.51 \\
23
+ TabM (D) & 1375${}_{-26,+30}$ & 0.280 & 18.3 & 11.8 & 0 & 12.6\% & 10.21 & 0.14 \\
24
+ TabPFNv2 (D) & 1366${}_{-31,+25}$ & 0.354 & 18.8 & 4.8 & 4 & 13.0\% & 3.37 & 0.32 \\
25
+ TorchMLP (T+E) & 1364${}_{-28,+29}$ & 0.233 & 19.1 & 14.8 & 0 & 11.6\% & 2389.22 & 2.16 \\
26
+ RealMLP (T) & 1357${}_{-27,+29}$ & 0.188 & 19.4 & 16.7 & 0 & 12.0\% & 6519.69 & 0.53 \\
27
+ EBM (T+E) & 1356${}_{-28,+26}$ & 0.188 & 19.3 & 13.5 & 0 & 14.9\% & 914.23 & 0.22 \\
28
+ FastaiMLP (T+E) & 1323${}_{-29,+23}$ & 0.203 & 21.0 & 12.4 & 0 & 14.6\% & 618.90 & 4.77 \\
29
+ ModernNCA (D) & 1306${}_{-28,+26}$ & 0.140 & 21.8 & 12.3 & 1 & 14.7\% & 14.78 & 0.35 \\
30
+ EBM (T) & 1295${}_{-30,+26}$ & 0.130 & 22.4 & 17.9 & 0 & 15.6\% & 914.23 & 0.03 \\
31
+ EBM (D) & 1265${}_{-36,+29}$ & 0.143 & 23.9 & 11.4 & 1 & 16.6\% & 4.31 & 0.05 \\
32
+ RealMLP (D) & 1253${}_{-27,+23}$ & 0.088 & 24.5 & 21.0 & 0 & 14.6\% & 21.83 & 0.90 \\
33
+ XGBoost (D) & 1253${}_{-31,+29}$ & 0.116 & 24.5 & 19.3 & 0 & 14.1\% & 1.77 & 0.12 \\
34
+ ExtraTrees (T+E) & 1252${}_{-29,+24}$ & 0.110 & 24.6 & 16.8 & 0 & 15.8\% & 189.76 & 0.74 \\
35
+ TabDPT (D) & 1239${}_{-35,+28}$ & 0.180 & 25.1 & 8.1 & 2 & 15.5\% & 22.61 & 8.55 \\
36
+ TorchMLP (T) & 1237${}_{-26,+26}$ & 0.106 & 25.1 & 21.7 & 0 & 14.1\% & 2389.22 & 0.15 \\
37
+ FastaiMLP (T) & 1220${}_{-29,+28}$ & 0.093 & 26.1 & 20.7 & 0 & 16.6\% & 618.90 & 0.30 \\
38
+ RandomForest (T+E) & 1214${}_{-24,+25}$ & 0.105 & 26.3 & 14.4 & 0 & 16.6\% & 323.74 & 0.74 \\
39
+ LightGBM (D) & 1198${}_{-28,+29}$ & 0.087 & 27.0 & 23.6 & 0 & 15.4\% & 1.79 & 0.12 \\
40
+ ExtraTrees (T) & 1196${}_{-33,+24}$ & 0.080 & 27.1 & 17.2 & 0 & 17.2\% & 189.76 & 0.08 \\
41
+ RandomForest (T) & 1159${}_{-33,+33}$ & 0.078 & 28.8 & 16.5 & 0 & 17.8\% & 323.74 & 0.08 \\
42
+ TorchMLP (D) & 1082${}_{-30,+22}$ & 0.023 & 32.0 & 29.3 & 0 & 19.3\% & 6.83 & 0.15 \\
43
+ FastaiMLP (D) & 1053${}_{-31,+29}$ & 0.031 & 33.1 & 29.9 & 0 & 22.1\% & 2.91 & 0.37 \\
44
+ RandomForest (D) & 1000${}_{-0,+0}$ & 0.013 & 34.8 & 32.9 & 0 & 24.1\% & 0.38 & 0.04 \\
45
+ Linear (T+E) & 998${}_{-37,+26}$ & 0.042 & 34.9 & 25.5 & 0 & 29.2\% & 51.79 & 0.22 \\
46
+ Linear (T) & 962${}_{-28,+24}$ & 0.027 & 36.1 & 30.6 & 0 & 30.1\% & 51.79 & 0.08 \\
47
+ Linear (D) & 951${}_{-30,+28}$ & 0.019 & 36.3 & 27.8 & 0 & 31.1\% & 1.61 & 0.10 \\
48
+ ExtraTrees (D) & 915${}_{-31,+32}$ & 0.010 & 37.3 & 34.3 & 0 & 26.7\% & 0.25 & 0.04 \\
49
+ KNN (T+E) & 688${}_{-46,+39}$ & 0.000 & 41.7 & 41.4 & 0 & 48.5\% & 2.97 & 0.19 \\
50
+ KNN (T) & 605${}_{-45,+48}$ & 0.000 & 42.7 & 42.5 & 0 & 50.3\% & 2.97 & 0.04 \\
51
+ KNN (D) & 462${}_{-71,+70}$ & 0.000 & 43.9 & 43.7 & 0 & 58.7\% & 0.07 & 0.02 \\
52
+ \bottomrule
53
+ \end{tabular}
data/full-imputed-cls/tabarena_leaderboard.csv ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ method,time_train_s,time_infer_s,time_train_s_per_1K,time_infer_s_per_1K,normalized-error,normalized-error-task,imputed,champ_delta,loss_rescaled,time_train_s_rescaled,time_infer_s_rescaled,rank,median_metric_error,median_time_train_s,median_time_infer_s,median_time_train_s_per_1K,median_time_infer_s_per_1K,median_normalized-error,median_normalized-error-task,median_imputed,median_champ_delta,median_loss_rescaled,median_time_train_s_rescaled,median_time_infer_s_rescaled,median_rank,rank=1_count,rank=2_count,rank=3_count,rank>3_count,elo,elo+,elo-,winrate,mrr
2
+ TABM_GPU (tuned + ensemble),34341.376551290836,9.60910518720136,4223.58680397795,3.4317121842487635,0.4949870968434646,0.5299923578456494,0.0,0.08239957044412496,0.05800528924519605,45864.929770826835,134.55326674312073,9.644736842105264,0.17047,8245.070318195554,2.6588550435172187,2466.2108648716858,1.5022465694891438,0.4807674997729695,0.5356016471758118,0.0,0.0374695276183053,0.02990990162448854,39661.48674821285,121.43882105497326,9.0,3,2,2,31,1574.1,27.7,27.1,0.8035287081339713,0.22390813053432343
3
+ AutoGluon 1.3 (4h),7918.9967654708535,21.9962719316371,2834.6060407147975,3.0873029091200266,0.4234797507066772,0.42851773721350084,0.0,0.06891082208685434,0.03901042356832644,31011.170971980166,263.893057970457,9.710526315789474,0.159155,7086.632828738954,3.4718479580349393,1322.7174946761893,2.35500290690449,0.33567162503552783,0.4181224156574922,0.0,0.03226297629046343,0.018303111934730745,23372.022224152715,142.27612475951975,6.0,6,3,1,28,1573.4,30.0,33.4,0.8020334928229665,0.3009017525052929
4
+ REALMLP (tuned + ensemble),88520.97457650169,55.83305884453288,9129.519936677754,18.43403910125304,0.5303101151258073,0.5509587531175434,0.0,0.08216963958005759,0.04917872511787768,141789.21603781544,866.2933837817693,10.460526315789474,0.16568,30350.410282479395,23.161008212301468,6519.687377737515,10.838853332215592,0.4880190327087218,0.5530787180349611,0.0,0.046844798741680516,0.023927651830585747,114204.24024987879,769.6999972028425,9.25,0,1,1,36,1552.0,31.2,29.1,0.784988038277512,0.1359044319221565
5
+ GBM (tuned + ensemble),2957.8209862183407,11.98416593290909,759.1477152793035,2.5417865978850473,0.579228860288798,0.5992528140715053,0.0,0.09787395108553403,0.0587719228886129,8568.794143617388,184.93302208876983,11.131578947368421,0.16698000000000002,1552.8742877377404,3.4757837878333198,382.05361557599804,1.4876036641335277,0.6139242992830947,0.6320945623165324,0.0,0.050681941850522993,0.0219954442643497,7514.342598912193,103.35126359750211,10.0,1,1,2,34,1536.6,25.3,29.4,0.7697368421052632,0.15602996440606
6
+ TABICL_GPU (default),107.96023476381747,19.280086713506464,9.625347263659664,2.230965763361112,0.4883393558984766,0.5463468792496827,0.05263157894736842,0.0794222885606876,0.05664734932761992,168.76491644336255,230.09998232516136,11.394736842105264,0.17207,25.732762111557854,3.449363695250617,8.684246340890724,1.7433667301105085,0.49313937962268345,0.5681798960380293,0.0,0.03858333399786745,0.018532235328146802,137.81489160855577,127.65482709424812,10.0,6,4,1,27,1529.5,22.7,30.1,0.763755980861244,0.2848797584062183
7
+ TABM_GPU (tuned),34341.376551290836,1.0683778852050068,4223.58680397795,0.37888409513825755,0.580460857439686,0.5850736457317778,0.0,0.0927595492056652,0.07202518163358282,45864.929770826835,13.890136033418576,13.223684210526315,0.172585,8245.070318195554,0.2723346948623657,2466.2108648716858,0.17557376557934842,0.5594681994427791,0.6408535524589001,0.0,0.04800849593484757,0.03209545871697756,39661.48674821285,10.546435553124088,14.0,1,3,2,32,1487.1,28.6,29.5,0.722188995215311,0.15718600055013962
8
+ CAT (tuned + ensemble),16504.07579820414,2.581779239609925,3201.0265707590415,0.8915752484646539,0.6052324431308997,0.6079016429921736,0.0,0.09115569260254941,0.05171280995480023,27193.868813721343,44.08651163500922,13.552631578947368,0.16040500000000002,5046.900271190538,1.2630292971928916,1372.9411122807264,0.5562989902961428,0.6047135908900858,0.6538224316024461,0.0,0.05787378892595674,0.023358345198772973,20541.46486167592,37.76547447057217,13.0,0,1,3,34,1479.0,23.4,25.1,0.7147129186602871,0.11519163508706867
9
+ CAT (tuned),16504.07579820414,0.42638187025025576,3201.0265707590415,0.12024042770987867,0.6205816792872123,0.6195401164987226,0.0,0.09330164070819684,0.05138375883564522,27193.868813721343,6.229177098348047,13.881578947368421,0.161705,5046.900271190538,0.12877851062350804,1372.9411122807264,0.07385371803542701,0.6259023413364113,0.6684527369967126,0.0,0.05645008601114826,0.027822328565894415,20541.46486167592,4.9725379405984995,14.0,1,2,1,34,1469.3,24.3,26.2,0.7072368421052632,0.14306281114130998
10
+ GBM (tuned),2957.8209862183407,1.875632255676894,759.1477152793035,0.5049369881097493,0.664414327281209,0.6568405029860219,0.0,0.10551185264608158,0.06848969063218771,8568.794143617388,32.37091249654915,14.302631578947368,0.16877999999999999,1552.8742877377404,0.5045170254177518,382.05361557599804,0.2538713244201605,0.7037360500796572,0.6730961643801268,0.0,0.05207128139882822,0.031112468640158004,7514.342598912193,15.175591971121808,12.5,0,0,0,38,1463.4,29.5,28.3,0.6976674641148325,0.08286433502159408
11
+ TABPFNV2_GPU (tuned + ensemble),10002.759252123178,101.4355180454533,2653.4255131981217,44.749282859570734,0.5025912169508552,0.5552745352373752,0.3157894736842105,0.09695040281680861,0.08124534064551475,49565.00539849654,3444.4876051657507,14.671052631578947,0.17261500000000002,2077.023049354553,12.760541562239329,3008.2157047151595,20.848616639963154,0.508383993403513,0.5777919166792829,0.0,0.04061436251124223,0.031106953519191773,28624.579287895787,825.5109643363919,8.5,8,3,2,25,1453.8,24.8,30.9,0.6892942583732058,0.3228293379761294
12
+ CAT (default),220.2627424415789,0.2710267032099049,109.9732905896213,0.13662992734425208,0.6377213723455996,0.653981819624179,0.0,0.10281875743582664,0.05525161599592243,404.9759898295662,6.476751807794606,14.68421052631579,0.16373,18.264583627382912,0.17750852637820774,5.723546572951673,0.0761539571798428,0.6679727149434583,0.668034897235229,0.0,0.05481736123368691,0.025874497523709752,103.69107151573829,5.120402547941957,16.0,1,3,1,33,1454.1,22.2,27.3,0.6889952153110048,0.14085028107005457
13
+ XGB (tuned + ensemble),5957.374200563333,6.456417350253166,1167.526219278486,2.7907218103162132,0.6678696607918362,0.667447365405449,0.0,0.1067022658124541,0.0693895201734448,10832.944520166355,137.58836217438702,14.763157894736842,0.165745,1680.0658507664998,2.2809726662105985,685.86510540535,1.4547593315263065,0.7199373388598738,0.7259505715046674,0.0,0.0603611320440825,0.027448205694685396,8251.297786111467,74.47019952683146,13.0,0,1,1,36,1452.0,30.3,27.2,0.687200956937799,0.1077066830171761
14
+ MNCA_GPU (tuned),57356.284085895306,20.170870465214488,5990.817791505437,2.0485901061394993,0.7110105277591534,0.6595940582554153,0.0,0.1050159299928894,0.0724200510289198,80456.91211763977,129.4389431731015,16.526315789473685,0.17054999999999998,14186.536935488384,0.6020842525694106,4879.890404506269,0.5247194359730172,0.7789380151687756,0.6741481297678236,0.0,0.06755655006003625,0.04344719805270737,66956.02864547497,27.735396922747526,16.5,1,0,0,37,1415.9,23.5,33.3,0.6471291866028708,0.10295238153020833
15
+ XGB (tuned),5957.374200563333,1.301870340352867,1167.526219278486,0.6678561539347552,0.7214356698637235,0.7066756243519319,0.0,0.11080653156459744,0.07384263953337422,10832.944520166355,28.47499330573137,16.63157894736842,0.16848000000000002,1680.0658507664998,0.3827125522825453,685.86510540535,0.2050912539994952,0.7533869940641466,0.7531012522750911,0.0,0.0694652487234973,0.03410738014755424,8251.297786111467,11.461364611937853,15.5,0,0,0,38,1412.1,27.2,25.8,0.6447368421052632,0.07527320270823155
16
+ MNCA_GPU (tuned + ensemble),57356.284085895306,531.7593351699455,5990.817791505437,50.07821547982156,0.6178778787330054,0.6105886884096879,0.0,0.10389701155615068,0.07981152020070442,80456.91211763977,3301.6213661597712,16.68421052631579,0.183035,14186.536935488384,14.17156207561493,4879.890404506269,8.743516387788919,0.6004419642795275,0.5800963441226324,0.0,0.06497462948315735,0.039747452446492706,66956.02864547497,548.1975258046007,12.5,0,2,4,32,1410.4,26.7,27.6,0.6435406698564593,0.13300821802463408
17
+ TABPFNV2_GPU (tuned),10002.759252123178,3.410806728176206,2653.4255131981217,1.555769686886014,0.6152128339751168,0.6368249005546318,0.3157894736842105,0.12130685228554555,0.0956967324044558,49565.00539849654,114.00749994252256,17.842105263157894,0.1868,2077.023049354553,0.5060818235079447,3008.2157047151595,0.5144277113236544,0.6861393226426085,0.651413564581804,0.0,0.08561151707807774,0.042273687584978896,28624.579287895787,25.570859321617846,13.0,1,8,1,28,1387.8,32.2,29.9,0.6172248803827751,0.19384505458173865
18
+ TABM_GPU (default),150.0762373017289,1.2507152029645372,19.886819500646006,0.46487430096802723,0.7200913651969231,0.7318128476710409,0.0,0.1257035538283091,0.09205633408961793,189.61145131944258,14.042337078320946,18.32894736842105,0.17246,31.126562476158142,0.20260944763819377,10.213381764059356,0.1381032773929915,0.8315398727681542,0.7950654471852272,0.0,0.06154427249600947,0.03281090889681732,144.96049255349743,10.968725907290855,18.0,0,0,1,37,1375.0,29.4,25.1,0.6061602870813397,0.08448030842994195
19
+ TABPFNV2_GPU (default),11.400364582092442,0.8958970822786029,4.227300497658887,0.4575723059305235,0.646401200043383,0.6913643236884485,0.3157894736842105,0.13032850434642715,0.10434014574898658,53.73712299744585,29.471362460513575,18.842105263157894,0.1886,7.994279013739691,0.2908047080039978,3.368600991426515,0.3152861168047789,0.777395370386663,0.7325492470622512,0.0,0.07929605277196083,0.04313507441518306,41.34560285308973,17.75894630310718,17.0,4,1,4,29,1366.1,24.9,30.7,0.5944976076555024,0.21051957029694982
20
+ NN_TORCH (tuned + ensemble),24331.947126566527,16.31368946276213,3050.9102763481856,4.325404104362444,0.7674791714524483,0.751965702834341,0.0,0.11637811814114107,0.08020239923609776,56038.718047349466,227.24142067178357,19.05263157894737,0.17071999999999998,9097.789536105262,5.056680162747702,2389.2199648500327,2.157502904371376,0.8880043848397714,0.8073663594549582,0.0,0.06782479524801643,0.04484831234958845,44480.90343297912,173.17543399472862,19.5,0,0,0,38,1364.3,29.0,27.1,0.5897129186602871,0.06737424255513398
21
+ EBM (tuned + ensemble),36729.440274559965,1.3371900389766136,6141.104384884199,0.5036823041953499,0.811594312476448,0.8073262691422789,0.0,0.14906209202998957,0.10957472519228896,25271.27145534202,19.518642702178116,19.342105263157894,0.17122500000000002,2366.879786974854,0.4240463972091675,914.2329798556116,0.21634762578811195,0.9034396658321221,0.8509428943624828,0.0,0.0823298574360965,0.040258063765572304,15273.913117491418,11.254406005139426,19.0,0,0,1,37,1356.1,25.2,27.9,0.5831339712918661,0.07392904855395822
22
+ REALMLP (tuned),88520.97457650169,2.4448067613512454,9129.519936677754,0.9659323148202512,0.8116906333552832,0.7529096930561373,0.0,0.11967014747000536,0.08502935951276701,141789.21603781544,39.671058690327705,19.355263157894736,0.17194500000000001,30350.410282479395,0.9911958641476102,6519.687377737515,0.5341468926001405,0.8954815082143113,0.7906338583190218,0.0,0.076222073390521,0.04561771439992753,114204.24024987879,35.28258688191838,18.75,0,0,0,38,1356.9,28.2,26.5,0.5828349282296651,0.059857762488610834
23
+ FASTAI (tuned + ensemble),7309.51755415473,18.692008190266574,1376.1098802486467,8.473426897342513,0.7967114176056229,0.7935375376704039,0.0,0.14576367767903883,0.08697133573600783,18964.092381812123,455.5897128961598,21.013157894736842,0.178125,3087.37076303694,11.787012616793314,618.8953909329178,4.7655686359255345,0.9993616733332265,0.8596726175256504,0.0,0.08731644523471122,0.054465543302125864,15284.817189242676,443.8905026950689,22.75,0,1,0,37,1323.2,22.6,28.2,0.5451555023923444,0.08034444085096946
24
+ MNCA_GPU (default),304.22019695963775,10.484658345144394,17.60828380729721,1.3061868722894623,0.8600476423581978,0.8133618407225838,0.0,0.14684370064110588,0.09623286753523605,254.62493914649235,74.63976386946672,21.789473684210527,0.18519,31.500762327512106,0.5732622504234314,14.777266169000828,0.34634581634079226,1.0,0.893695279008777,0.0,0.09230605494417649,0.051103226670839136,209.09978531409226,23.91921150117286,23.0,1,0,0,37,1306.1,25.7,27.4,0.527511961722488,0.08099601234817519
25
+ EBM (tuned),36729.440274559965,0.18373215324000308,6141.104384884199,0.08212434505580339,0.8701293455239967,0.8449855604868557,0.0,0.15617886229027844,0.11711713769734972,25271.27145534202,2.5019643980329747,22.42105263157895,0.17203000000000002,2366.879786974854,0.0449512971772088,914.2329798556116,0.02528246646038782,1.0,0.8943570495572675,0.0,0.08835084225135731,0.04420635043937865,15273.913117491418,1.252412448907763,23.5,0,0,0,38,1295.1,25.2,29.9,0.5131578947368421,0.05576760659451273
26
+ EBM (default),119.68166406294058,0.19009479611937763,11.429209024387047,0.10185762188607354,0.8572475476046651,0.8539911315322448,0.0,0.16604276757956396,0.12380006768435853,109.6134925105603,3.4110931424674527,23.894736842105264,0.17447000000000001,9.92618230978648,0.06371633741590713,4.31382445805991,0.0475851422516083,1.0,0.9282824912353611,0.0,0.09554880640873625,0.03755874966541475,60.70178540099199,2.7834768100426253,24.0,1,0,2,35,1264.9,28.3,35.7,0.4796650717703349,0.08753724330105657
27
+ REALMLP (default),302.86446626785903,3.0018129969200893,26.277581916467653,2.856467141391241,0.9122374081508348,0.85760098469969,0.0,0.14572889637854225,0.10212034328423684,472.82883048215837,115.85189921010567,24.5,0.17367,103.08245442973242,2.582352219687568,21.83278809042843,0.8957992336206358,1.0,0.8896440117833417,0.0,0.11770994919600164,0.06845697585035651,372.59928806765174,52.85349774079455,25.5,0,0,0,38,1253.4,23.0,26.4,0.4659090909090909,0.04751011701630104
28
+ XGB (default),13.116732126927516,0.5742131232518202,3.202512268619222,0.2981186068489448,0.8843514041095932,0.8510762104833869,0.0,0.14066462097580287,0.11671766984062094,31.46292861336276,13.4756369742995,24.5,0.17317,5.653352538744608,0.30113152662913,1.771208861779989,0.11707781619763814,1.0,0.9410060237993583,0.0,0.09869044613168698,0.06054796407572259,28.26053761224749,9.127662964815336,24.0,0,0,0,38,1253.3,28.1,30.7,0.4659090909090909,0.05191659220532364
29
+ XT (tuned + ensemble),1317.4209560674533,2.9519454171085915,472.65138083581655,1.3657584923642982,0.889559570547466,0.8556261516301992,0.0,0.1579271337121436,0.11679174830217959,4571.615940832833,75.60244638381447,24.57894736842105,0.17667500000000003,756.8230986197789,1.8136235740449693,189.76252609436062,0.7431041876698922,1.0,0.93225990863037,0.0,0.09299076955407681,0.0685642717298475,2805.66154207989,66.39288968996527,27.5,0,0,1,37,1251.6,23.8,28.6,0.46411483253588515,0.05948664377868326
30
+ TABDPT_GPU (default),171.71139350780967,66.09824930987163,27.724576482795502,22.626481529214185,0.8200159004748209,0.8098229820925923,0.0,0.1548400388440821,0.1161870388203104,481.10896045076544,1338.9171702872993,25.105263157894736,0.190385,97.80311637454562,28.07416233751509,22.609050986069803,8.552450841932743,1.0,0.9583506424916117,0.0,0.1092338236572048,0.046097964205681456,400.67828468381333,1123.8959746745188,30.0,2,0,3,33,1238.6,27.7,34.6,0.45215311004784686,0.12327697073991685
31
+ NN_TORCH (tuned),24331.947126566527,0.8716282456241854,3050.9102763481856,0.24396545036982029,0.8935160561232315,0.8422311335793125,0.0,0.14120278296028227,0.10508427239733291,56038.718047349466,12.138164397560297,25.144736842105264,0.17446499999999998,9097.789536105262,0.29952494303385413,2389.2199648500327,0.15177921475257505,1.0,0.9030726729865997,0.0,0.10379368194908523,0.06014400941853005,44480.90343297912,9.196372470124006,25.5,0,0,0,38,1237.2,25.5,25.3,0.451255980861244,0.046101906732064955
32
+ FASTAI (tuned),7309.51755415473,1.0496307073977955,1376.1098802486467,0.623937267000547,0.9066743837597047,0.8621224292232853,0.0,0.16578900249006634,0.10852452399743645,18964.092381812123,32.00078545496663,26.06578947368421,0.18023499999999998,3087.37076303694,0.8054822285970051,618.8953909329178,0.2978802219128553,1.0,0.9088593404454739,0.0,0.09789639604232409,0.0649858160039401,15284.817189242676,26.536834640421894,26.0,0,0,0,38,1220.2,27.5,28.1,0.43032296650717705,0.04823155314220717
33
+ RF (tuned + ensemble),2309.3465478268977,2.3587986137434753,541.3031953907538,1.2662572218585502,0.8949088487058612,0.8653921072161802,0.0,0.16581458532483917,0.12803665978914544,5371.163113535875,67.79453318661523,26.31578947368421,0.177925,871.1966819789675,1.9029027620951335,323.74369638605225,0.7428875097152683,1.0,0.976443274983263,0.0,0.10359622790389916,0.07479078376008455,4278.677975908691,61.67862848692378,28.5,0,1,1,36,1213.5,24.8,23.2,0.4246411483253589,0.06923937840225046
34
+ GBM (default),7.9087888545460165,0.5753568479889317,2.951996847158713,0.17011749256975625,0.9125347540697407,0.8867856543564054,0.0,0.15377361970482045,0.11510537200611927,31.85523047906877,10.81395814590769,27.026315789473685,0.172975,5.532836645179325,0.2585195038053725,1.7913477923414471,0.12049981156984965,1.0,0.953002789600119,0.0,0.11068698010353623,0.0625734900521432,25.045220341015206,6.549914554959342,28.0,0,0,0,38,1197.8,28.8,27.4,0.4084928229665072,0.04242553702308852
35
+ XT (tuned),1317.4209560674533,0.3043035832762021,472.65138083581655,0.1722314796858175,0.9201818360376706,0.887453690669917,0.0,0.1716073337665082,0.12727956433250776,4571.615940832833,8.349379059762423,27.11842105263158,0.17796,756.8230986197789,0.18769407272338867,189.76252609436062,0.07878183958882805,1.0,0.9660073423997149,0.0,0.10587649481110034,0.07275368947647169,2805.66154207989,8.013491457037578,30.0,0,1,0,37,1195.6,24.0,32.1,0.4063995215311005,0.05828850461349728
36
+ RF (tuned),2309.3465478268977,0.23789871352457861,541.3031953907538,0.15647241398955916,0.9224816586070888,0.8914900089794889,0.0,0.17793267481613345,0.14008678334835084,5371.163113535875,7.241889916329925,28.763157894736842,0.178915,871.1966819789675,0.17230602105458576,323.74369638605225,0.07643497412773152,1.0,0.9949804738019044,0.0,0.11788865245982966,0.07710393358859187,4278.677975908691,6.216263662191708,31.5,0,1,1,36,1159.2,32.8,32.1,0.36901913875598086,0.060732505395188326
37
+ NN_TORCH (default),48.66164081361559,0.6504810580733227,11.536659167937149,0.237589986723434,0.9768277299582552,0.9516276223313249,0.0,0.19345935921063773,0.14725824727620437,155.18773864824473,10.878820110101595,31.986842105263158,0.180355,26.916632894674937,0.2675716214709811,6.83469910157457,0.14703020953097523,1.0,0.9983912483912485,0.0,0.142508632176782,0.09058242754644441,137.05069981706868,8.619821917518276,33.0,0,0,0,38,1081.6,21.3,29.3,0.29575358851674644,0.0341695156824269
38
+ FASTAI (default),31.12571889106293,1.103970385504048,5.0919225272079345,0.5139922583887925,0.9692956872023926,0.9418353795989344,0.0,0.2205946249171078,0.17081144202844967,74.47424680202033,27.97257070113272,33.078947368421055,0.19183499999999998,12.713354892200893,0.7982388072543674,2.9120182447539116,0.36810695156439827,1.0,1.0,0.0,0.16584224786083723,0.11423976777713259,60.1261932941261,24.301698162325565,36.0,0,0,0,38,1052.8,28.1,30.9,0.27093301435406697,0.03346355889150736
39
+ RF (default),3.928520040972191,0.1435067986187182,0.8021093004373405,0.07125612411976835,0.9873793306417248,0.9675347593198196,0.0,0.24146620137116087,0.23100094778843142,6.102631035159317,3.7416299644570024,34.80263157894737,0.21025,1.1966572999954224,0.08589340580834282,0.3813090053938437,0.03721195658349352,1.0,1.0,0.0,0.17586669886220047,0.11692775501298655,5.480722222590385,3.4579154094346745,36.5,0,0,0,38,1000.0,0.0,0.0,0.23175837320574164,0.03044058622007152
40
+ LR (tuned + ensemble),310.9206490230839,1.856038474618343,112.43523004573552,0.6244822981818133,0.9583388751756515,0.9525027008333006,0.0,0.2917619246531289,0.2639449201736458,1090.6543235756506,24.154836057241496,34.94736842105263,0.20569500000000002,172.99803659651013,0.33067578077316284,51.78500762114817,0.22385943240534312,1.0,1.0,0.0,0.23092004636837282,0.1418450851884247,696.2608974821628,13.25530093456624,38.0,0,0,1,37,998.1,25.5,37.0,0.2284688995215311,0.039205008884686546
41
+ LR (tuned),310.9206490230839,0.5155129476597434,112.43523004573552,0.17331679222553462,0.9727265210610188,0.9599648989348959,0.0,0.30105734113761295,0.27391129557139166,1090.6543235756506,6.903044838007482,36.05263157894737,0.20751,172.99803659651013,0.12874411212073433,51.78500762114817,0.07805190196778514,1.0,1.0,0.0,0.23702586233900474,0.14460038903245942,696.2608974821628,4.422167155622043,38.5,0,0,0,38,961.6,23.9,27.7,0.20334928229665072,0.0327143779465347
42
+ LR (default),7.59960079534709,0.5285763780973111,2.68894957171299,0.18897855365485863,0.9811233759976523,0.9657118387068965,0.0,0.3109312407898901,0.298004116613438,27.747857838408066,7.876763349331487,36.3421052631579,0.21230500000000002,5.359276652336121,0.13779839674631755,1.6116061178401027,0.09774404154712064,1.0,1.0,0.0,0.23702577885376547,0.1541261802297627,18.023625361427616,4.735441569338942,40.0,0,0,1,37,950.6,27.1,29.7,0.19677033492822968,0.03600631416753817
43
+ XT (default),2.7359117016457675,0.18087529669031066,0.7550887156747417,0.07422230753863952,0.9896376739572407,0.9751584322493536,0.0,0.26658722117284267,0.26095559827613696,5.173741811459425,4.243951698357253,37.26315789473684,0.21292,1.01790091726515,0.09051434199015299,0.24605929188859293,0.04072451222400395,1.0,1.0,0.0,0.18424442460299273,0.13780186094528035,4.456264068369281,3.7424721126192138,39.0,0,0,0,38,914.9,32.0,30.5,0.17583732057416268,0.0291505121116296
44
+ KNN (tuned + ensemble),167.0455302492917,11.66904854404996,12.260943976874097,0.77695539036087,1.0,0.9962934098751769,0.13157894736842105,0.48462823969745605,0.5970228537293082,72.48328913141673,77.9475913060204,41.671052631578945,0.318405,9.821025305324131,0.2367298404375712,2.969713103492417,0.18997109296167614,1.0,1.0,0.0,0.42625485653324524,0.6596342220080722,56.9019152818522,12.487074071232104,43.0,0,0,0,38,688.4,38.7,45.5,0.0756578947368421,0.024178278159192945
45
+ KNN (tuned),167.0455302492917,1.8054817143936601,12.260943976874097,0.13106223823409818,1.0,0.9976590626673897,0.13157894736842105,0.5033620273790689,0.6430012930336472,72.48328913141673,12.511864612142434,42.671052631578945,0.322975,9.821025305324131,0.0851174063152737,2.969713103492417,0.040417757021976156,1.0,1.0,0.0,0.4549174904387682,0.7006933550921264,56.9019152818522,2.342964973116946,44.0,0,0,0,38,605.3,48.0,44.4,0.05293062200956938,0.02351625016097909
46
+ KNN (default),1.7449495283483762,0.22627578220869365,0.489346568018936,0.038714559202156204,1.0,1.0,0.13157894736842105,0.5871637410556744,0.939313827722613,1.0055419249185589,2.3649007838074403,43.921052631578945,0.382765,0.27595198154449463,0.036337282922532826,0.07126887487893994,0.021006283652748647,1.0,1.0,0.0,0.5463314318406584,1.0,1.0,1.2165713596834893,45.0,0,0,0,38,462.1,69.7,70.6,0.02452153110047847,0.022868529201343697
data/full-imputed-reg/leaderboard.tex ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{tabular}{llcccccrr}
2
+ \toprule
3
+ \textbf{Model} & \textbf{Elo ($\uparrow$)} & \textbf{Norm.} & \textbf{Avg.} & \textbf{Harm.} & \textbf{\#wins ($\uparrow$)} & \textbf{Improva-} & \textbf{Train time} & \textbf{Predict time} \\
4
+ & & \textbf{score ($\uparrow$)} & \textbf{rank ($\downarrow$)} & \textbf{mean} & & \textbf{bility ($\downarrow$)} & \textbf{per 1K [s]} & \textbf{per 1K [s]} \\
5
+ & & & & \textbf{rank ($\downarrow$)} & & & & \\
6
+ \midrule
7
+ AutoGluon 1.3 (4h) & \textcolor{gold}{\textbf{1795${}_{-55,+67}$}} & \textcolor{gold}{\textbf{0.740}} & \textcolor{gold}{\textbf{4.6}} & \textcolor{silver}{\textbf{2.5}} & 2 & \textcolor{silver}{\textbf{2.7\%}} & 1625.74 & 6.76 \\
8
+ RealMLP (T+E) & \textcolor{silver}{\textbf{1766${}_{-64,+61}$}} & \textcolor{silver}{\textbf{0.732}} & \textcolor{silver}{\textbf{5.3}} & 3.4 & 0 & \textcolor{gold}{\textbf{2.0\%}} & 7141.94 & 9.66 \\
9
+ ModernNCA (T+E) & \textcolor{bronze}{\textbf{1632${}_{-52,+51}$}} & 0.625 & \textcolor{bronze}{\textbf{8.3}} & \textcolor{bronze}{\textbf{2.8}} & \textcolor{silver}{\textbf{3}} & 3.8\% & 3779.52 & 7.69 \\
10
+ TabDPT (D) & 1620${}_{-62,+53}$ & \textcolor{bronze}{\textbf{0.651}} & 8.8 & \textcolor{gold}{\textbf{2.3}} & \textcolor{gold}{\textbf{5}} & \textcolor{bronze}{\textbf{2.9\%}} & 22.53 & 8.55 \\
11
+ CatBoost (T+E) & 1616${}_{-51,+59}$ & 0.547 & 8.9 & 7.2 & 0 & 4.5\% & 3552.96 & 0.97 \\
12
+ LightGBM (T+E) & 1609${}_{-53,+58}$ & 0.542 & 9.2 & 7.1 & 0 & 5.0\% & 700.15 & 9.32 \\
13
+ CatBoost (T) & 1568${}_{-46,+64}$ & 0.524 & 10.3 & 6.9 & 0 & 4.6\% & 3552.96 & 0.10 \\
14
+ TabM (T+E) & 1562${}_{-64,+54}$ & 0.494 & 10.5 & 6.5 & 0 & 3.3\% & 4158.29 & 1.41 \\
15
+ XGBoost (T+E) & 1496${}_{-51,+43}$ & 0.447 & 12.8 & 12.3 & 0 & 5.5\% & 834.93 & 2.61 \\
16
+ LightGBM (T) & 1490${}_{-50,+55}$ & 0.445 & 12.9 & 10.8 & 0 & 5.6\% & 700.15 & 0.97 \\
17
+ XGBoost (T) & 1474${}_{-46,+46}$ & 0.414 & 13.7 & 13.1 & 0 & 5.6\% & 834.93 & 0.39 \\
18
+ ModernNCA (T) & 1446${}_{-49,+52}$ & 0.360 & 14.6 & 7.2 & 0 & 5.9\% & 3779.52 & 0.40 \\
19
+ TabM (T) & 1440${}_{-54,+39}$ & 0.392 & 15.0 & 11.6 & 0 & 4.3\% & 4158.29 & 0.17 \\
20
+ CatBoost (D) & 1439${}_{-57,+48}$ & 0.400 & 15.0 & 11.5 & 0 & 6.2\% & 10.89 & 0.09 \\
21
+ RealMLP (T) & 1405${}_{-53,+53}$ & 0.350 & 16.4 & 13.9 & 0 & 4.6\% & 7141.94 & 0.39 \\
22
+ TabPFNv2 (T+E) & 1381${}_{-47,+55}$ & 0.414 & 17.2 & 3.1 & \textcolor{silver}{\textbf{3}} & 5.1\% & 4223.87 & 27.54 \\
23
+ ModernNCA (D) & 1340${}_{-54,+50}$ & 0.216 & 18.8 & 14.1 & 0 & 7.4\% & 15.50 & 0.30 \\
24
+ TabM (D) & 1329${}_{-48,+53}$ & 0.300 & 19.2 & 15.6 & 0 & 6.0\% & 13.32 & 0.13 \\
25
+ TorchMLP (T+E) & 1306${}_{-57,+46}$ & 0.182 & 20.2 & 14.6 & 0 & 7.6\% & 4608.59 & 1.23 \\
26
+ TabPFNv2 (T) & 1300${}_{-55,+53}$ & 0.287 & 20.6 & 8.4 & 0 & 6.2\% & 4223.87 & 0.45 \\
27
+ RealMLP (D) & 1284${}_{-48,+47}$ & 0.152 & 21.3 & 18.4 & 0 & 7.0\% & 21.86 & 0.84 \\
28
+ ExtraTrees (T+E) & 1272${}_{-60,+53}$ & 0.162 & 21.5 & 13.6 & 0 & 10.0\% & 158.22 & 0.84 \\
29
+ LightGBM (D) & 1264${}_{-54,+53}$ & 0.091 & 21.9 & 21.3 & 0 & 8.1\% & 2.11 & 0.27 \\
30
+ ExtraTrees (T) & 1255${}_{-53,+42}$ & 0.136 & 22.3 & 16.9 & 0 & 10.3\% & 158.22 & 0.15 \\
31
+ TabPFNv2 (D) & 1231${}_{-61,+59}$ & 0.238 & 23.3 & 11.5 & 0 & 7.6\% & 2.80 & 0.31 \\
32
+ TorchMLP (T) & 1230${}_{-47,+46}$ & 0.131 & 23.3 & 20.1 & 0 & 8.4\% & 4608.59 & 0.10 \\
33
+ XGBoost (D) & 1215${}_{-46,+42}$ & 0.114 & 24.1 & 21.5 & 0 & 8.8\% & 2.24 & 0.24 \\
34
+ RandomForest (T+E) & 1203${}_{-48,+46}$ & 0.076 & 24.5 & 22.4 & 0 & 10.9\% & 515.73 & 0.77 \\
35
+ RandomForest (T) & 1153${}_{-58,+49}$ & 0.055 & 26.2 & 24.5 & 0 & 11.4\% & 515.73 & 0.12 \\
36
+ EBM (T+E) & 1136${}_{-61,+46}$ & 0.171 & 27.0 & 13.4 & 0 & 13.7\% & 1890.68 & 0.13 \\
37
+ ExtraTrees (D) & 1100${}_{-63,+56}$ & 0.069 & 28.1 & 25.0 & 0 & 12.2\% & 0.47 & 0.06 \\
38
+ EBM (T) & 1092${}_{-57,+50}$ & 0.150 & 28.5 & 17.0 & 0 & 14.2\% & 1890.68 & 0.01 \\
39
+ FastaiMLP (T+E) & 1040${}_{-50,+62}$ & 0.024 & 30.0 & 28.0 & 0 & 12.2\% & 540.06 & 2.67 \\
40
+ TorchMLP (D) & 1038${}_{-63,+58}$ & 0.017 & 30.2 & 28.2 & 0 & 11.9\% & 20.48 & 0.08 \\
41
+ EBM (D) & 1034${}_{-67,+44}$ & 0.109 & 30.6 & 27.7 & 0 & 15.1\% & 6.33 & 0.04 \\
42
+ RandomForest (D) & 1000${}_{-0,+0}$ & 0.000 & 31.3 & 30.7 & 0 & 12.9\% & 0.53 & 0.06 \\
43
+ FastaiMLP (T) & 992${}_{-65,+49}$ & 0.014 & 31.7 & 30.4 & 0 & 12.7\% & 540.06 & 0.32 \\
44
+ FastaiMLP (D) & 858${}_{-70,+53}$ & 0.000 & 34.9 & 34.3 & 0 & 17.1\% & 2.60 & 0.39 \\
45
+ KNN (T+E) & 532${}_{-96,+78}$ & 0.000 & 39.8 & 39.6 & 0 & 36.1\% & 2.43 & 0.14 \\
46
+ Linear (T+E) & 489${}_{-95,+53}$ & 0.000 & 40.3 & 40.2 & 0 & 35.4\% & 45.74 & 0.11 \\
47
+ KNN (T) & 442${}_{-135,+68}$ & 0.000 & 40.8 & 40.6 & 0 & 36.8\% & 2.43 & 0.03 \\
48
+ Linear (T) & 421${}_{-95,+82}$ & 0.000 & 40.9 & 40.8 & 0 & 35.6\% & 45.74 & 0.05 \\
49
+ Linear (D) & 290${}_{-113,+87}$ & 0.000 & 42.2 & 42.1 & 0 & 38.1\% & 1.19 & 0.09 \\
50
+ KNN (D) & 239${}_{-146,+87}$ & 0.000 & 42.6 & 42.4 & 0 & 40.8\% & 0.04 & 0.02 \\
51
+ \bottomrule
52
+ \end{tabular}
data/full-imputed-reg/tabarena_leaderboard.csv ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ method,time_train_s,time_infer_s,time_train_s_per_1K,time_infer_s_per_1K,normalized-error,normalized-error-task,imputed,champ_delta,loss_rescaled,time_train_s_rescaled,time_infer_s_rescaled,rank,median_metric_error,median_time_train_s,median_time_infer_s,median_time_train_s_per_1K,median_time_infer_s_per_1K,median_normalized-error,median_normalized-error-task,median_imputed,median_champ_delta,median_loss_rescaled,median_time_train_s_rescaled,median_time_infer_s_rescaled,median_rank,rank=1_count,rank=2_count,rank=3_count,rank>3_count,elo,elo+,elo-,winrate,mrr
2
+ AutoGluon 1.3 (4h),10053.09853225386,65.70725388180496,3138.716102354025,8.201870690838728,0.2604881581536672,0.2971194348020468,0.0,0.026995880876839205,0.03762065178367739,79227.35072832079,1344.7158520024777,4.615384615384615,4.16484,12938.261539538702,10.17258334159851,1625.738447135909,6.759745919437983,0.23036881663552905,0.30366624015393634,0.0,0.015883109174547605,0.016643068102528722,85166.92529682687,517.5224332581762,4.0,2,3,1,7,1795.1,67.0,54.8,0.9159212880143113,0.3928904428904429
3
+ REALMLP (tuned + ensemble),66543.44573790843,35.91296550086421,7325.429690273165,14.32081689268905,0.2681319612182075,0.3168526714039812,0.0,0.019775498611648384,0.029999838203468132,290863.52338623884,790.182850123545,5.3076923076923075,4.13253,35612.93265602324,25.25520912806193,7141.940159399529,9.664030993978182,0.21463277228840488,0.3314660386375113,0.0,0.015498759976369625,0.017118693739991864,262171.8658686582,808.3809230744637,3.0,0,4,3,6,1765.6,60.7,63.8,0.8998211091234347,0.2966880341880342
4
+ MNCA_GPU (tuned + ensemble),31759.101002019288,48.99773550746787,6166.048475961011,17.643273328787647,0.37486519261010337,0.3801843857676789,0.0,0.038093035353910405,0.03729025388197337,186080.1526868885,677.6872983460363,8.307692307692308,4.48697,16310.556293937894,10.227302259869045,3779.5248398651206,7.690422738079533,0.33564273912263787,0.35100871313945403,0.0,0.019057076597787104,0.02404483039332963,198038.2195884601,507.5335424076248,7.0,3,1,1,8,1632.5,51.0,51.8,0.8300536672629696,0.35811118705855544
5
+ TABDPT_GPU (default),150.02111775732448,54.38835432835114,28.728808967170515,25.042434382939593,0.34889596641909676,0.3868397999855826,0.0,0.0290119218252697,0.03451422020936026,859.9949957462201,1916.4746059233426,8.846153846153847,4.26747,137.4807067182329,29.239719518025716,22.52690614988171,8.547073882575031,0.2832974905649298,0.3551487188815256,0.0,0.024988993973347418,0.022011234994563023,904.9754515353668,1821.0928942426492,8.0,5,0,0,8,1620.5,52.9,61.7,0.817531305903399,0.44424741924741923
6
+ CAT (tuned + ensemble),30302.612278386874,4.803950041991014,6846.603031359736,1.23549623448159,0.4533481852287783,0.4926936982252365,0.0,0.04475906131711523,0.05586568278238831,150000.99966111325,116.73562230823504,8.923076923076923,4.20864,22090.5574801498,2.685193909539117,3552.958864906998,0.9657383741190037,0.4197753110205541,0.5095036442102125,0.0,0.0214910315460225,0.0360010253274791,164350.82166574517,80.04598787510639,9.0,0,0,1,12,1615.9,58.2,51.0,0.815742397137746,0.13981452645420256
7
+ GBM (tuned + ensemble),3469.6354441180188,51.50608464469258,806.792675338677,8.57889161766087,0.4578704418351117,0.4915767713552377,0.0,0.049628721858481745,0.06009436123338137,20529.34803212188,1135.938430886033,9.153846153846153,4.21165,3055.419878217909,17.84196005927192,700.1537746143185,9.321818212785747,0.41469312312474516,0.4239687696189567,0.0,0.026696516466175324,0.03428340330572247,21786.570035668443,556.5446710812895,7.0,0,0,0,13,1608.9,57.6,52.5,0.8103756708407871,0.14022351917088757
8
+ CAT (tuned),30302.612278386874,0.5550543483505901,6846.603031359736,0.16909210735792113,0.47626653515508005,0.5114892609915386,0.0,0.04596154469633722,0.059493084445231575,150000.99966111325,13.40589862804556,10.26923076923077,4.23064,22090.5574801498,0.3789627022213406,3552.958864906998,0.10450043094654878,0.5098664416231588,0.5042459184199164,0.0,0.026006470182449948,0.03741223775007456,164350.82166574517,10.277501137463553,10.0,0,1,1,11,1568.5,63.6,45.8,0.7844364937388193,0.14579928618390156
9
+ TABM_GPU (tuned + ensemble),42663.03407314721,6.401903104782104,7337.2216133773,2.4450322937588758,0.5064842509501634,0.5097062474762206,0.0,0.03285461585754071,0.07923986245243698,180683.6213219068,120.23686774247648,10.538461538461538,4.1458,20820.912871148852,3.0152715841929116,4158.291053548661,1.4096720886484275,0.41808698686448353,0.44811012371201403,0.0,0.031023728849073895,0.02730307401826167,166291.5239595113,107.26839105814781,6.0,0,0,1,12,1562.3,53.2,63.1,0.778175313059034,0.15363693425074498
10
+ XGB (tuned + ensemble),6384.208144447945,12.513759183476113,1409.0815132938305,4.086239880001324,0.5528907467847085,0.5842672042727117,0.0,0.05501928538394466,0.06767162618009932,26398.72664659111,353.2622484982949,12.846153846153847,4.22219,2596.930354913076,6.618133616447449,834.9300717202715,2.614265349176195,0.6061614351314211,0.632068655632026,0.0,0.03390767360912417,0.047976586381100285,26370.57574541831,164.97865636299682,13.0,0,0,0,13,1495.9,43.0,50.2,0.7245080500894454,0.0815581414563315
11
+ GBM (tuned),3469.6354441180188,7.385669826034806,806.792675338677,1.273710690539978,0.5553229125473071,0.5751848987218714,0.0,0.0557293790854645,0.07273694432227554,20529.34803212188,170.85098129275468,12.923076923076923,4.23482,3055.419878217909,2.690451833936903,700.1537746143185,0.9682498776019389,0.5552965190223861,0.5297118411020986,0.0,0.027972044011269404,0.03922298758674819,21786.570035668443,130.77892053638826,12.0,0,0,0,13,1490.1,54.7,49.3,0.7227191413237924,0.09231764967059085
12
+ XGB (tuned),6384.208144447945,2.5463058025409016,1409.0815132938305,0.7958717570786976,0.5861202672596529,0.6108958009331424,0.0,0.056497224579359025,0.07175664097406265,26398.72664659111,74.35901041305158,13.73076923076923,4.23559,2596.930354913076,1.7797584003872342,834.9300717202715,0.3883258596259137,0.6620816436761664,0.6284024475165967,0.0,0.03317270939304562,0.04927060675426248,26370.57574541831,37.05707269936294,12.0,0,0,0,13,1474.5,45.4,45.1,0.7039355992844365,0.07642316833493304
13
+ MNCA_GPU (tuned),31759.101002019288,2.180037997319148,6166.048475961011,0.7699041848491119,0.640014197538291,0.6006804484966409,0.0,0.05893528628182787,0.07225555979752735,186080.1526868885,32.66971509741366,14.615384615384615,4.74895,16310.556293937894,0.48738079600863987,3779.5248398651206,0.3958729871859153,0.6568741279318248,0.634131414678689,0.0,0.027829209062692706,0.05013010527584701,198038.2195884601,22.639832589759934,16.0,0,1,2,10,1446.4,51.4,48.7,0.6833631484794276,0.13858206746510898
14
+ TABM_GPU (tuned),42663.03407314721,0.6790118826760185,7337.2216133773,0.2826346699151617,0.6078346969283082,0.6089177873032084,0.0,0.04291459744083105,0.1000977395685804,180683.6213219068,13.142332198971314,15.0,4.26932,20820.912871148852,0.3543446593814426,4158.291053548661,0.16724776809008424,0.5483643325463751,0.5720139858507244,0.0,0.039608740417227284,0.035365730806074164,166291.5239595113,12.524825096296247,13.0,0,0,0,13,1440.3,38.9,53.5,0.6744186046511628,0.08607273216026531
15
+ CAT (default),101.69209800418625,0.27084102752881173,23.09400681118272,0.13269262448628016,0.5995482740998302,0.6304472090224245,0.0,0.06246023871361243,0.07708318676535167,496.3567653671193,9.26273127760114,15.038461538461538,4.21395,62.699359814325966,0.2736650307973226,10.889876924735699,0.09199146861016888,0.5823029883647647,0.640700383892448,0.0,0.030438989353365198,0.03712451320809096,412.72250349454265,8.72239577968297,13.0,0,0,0,13,1438.7,47.7,56.4,0.6735241502683363,0.08685217342752263
16
+ REALMLP (tuned),66543.44573790843,1.7411092819311682,7325.429690273165,0.8126671252939139,0.6503917944927049,0.6335765314526475,0.0,0.04638057342567833,0.08360703671071543,290863.52338623884,43.982520909683025,16.384615384615383,4.34383,35612.93265602324,1.0202796989017062,7141.940159399529,0.394499832888234,0.6897823031948702,0.6773094419226776,0.0,0.042257970846457216,0.05482988743609004,262171.8658686582,39.332347707117826,17.0,0,0,0,13,1404.9,52.4,52.4,0.6422182468694096,0.07190526635498172
17
+ TABPFNV2_GPU (tuned + ensemble),7396.527001981857,78.03043969614893,3418.770967200307,56.25472027093109,0.5864638170450713,0.6417803537075669,0.46153846153846156,0.05066211692688948,0.09308967259297021,101895.60372403836,4530.894138806997,17.192307692307693,4.11819,3945.7989165067675,13.798940539360046,4223.8673583405725,27.542795487744606,0.7230870148989033,0.7944604017471419,0.0,0.03408450486195502,0.03467029575770307,99491.49864758716,2157.1863969834576,19.0,3,1,1,8,1380.7,54.4,46.1,0.6234347048300537,0.31937392494449274
18
+ MNCA_GPU (default),99.58075828980176,2.0463109099966847,18.34482305470451,0.7039566701063034,0.7844574247003018,0.7513144031861464,0.0,0.07422761878566567,0.09797180456330291,547.7971597202185,27.012858819060174,18.76923076923077,4.94033,49.107323222690155,0.41290783882141113,15.50085128260229,0.29868905742963153,0.9390070942659281,0.8904304199346637,0.0,0.03934956674595991,0.09658469357099869,536.5510136544884,15.897990578305649,18.0,0,0,0,13,1340.3,49.6,53.2,0.5867620751341681,0.07094113282642321
19
+ TABM_GPU (default),109.08048485323914,0.5565733836247371,30.11061011517182,0.2424694191265854,0.6999854926260701,0.720056022702389,0.0,0.060357587291533545,0.131331635834194,580.6229196238922,9.300928058329742,19.23076923076923,4.27071,65.0270922978719,0.1736939483218723,13.315938751381204,0.13061717308512005,0.6917071760987948,0.6600858527247827,0.0,0.05131334419415079,0.05222355107697133,354.8698900002396,7.016702417594013,17.0,0,0,0,13,1329.0,52.5,47.2,0.5760286225402504,0.06404901956439267
20
+ NN_TORCH (tuned + ensemble),44960.17932087947,4.03415755573501,5714.412959426929,1.3298489659738517,0.8184873120659005,0.8128616776400956,0.0,0.07610055505274997,0.12013496922800454,220430.49479235077,97.5953017172201,20.23076923076923,4.65351,15497.21247045199,2.8239229255252414,4608.594420268999,1.2325370779691025,0.9269135314055867,0.8896068156264655,0.0,0.05961664423383828,0.08340398612928321,177697.9133637009,96.53385472639289,21.0,0,0,0,13,1306.3,46.0,56.5,0.552772808586762,0.06851055964209914
21
+ TABPFNV2_GPU (tuned),7396.527001981857,3.794137261464045,3418.770967200307,1.977808999126841,0.7134155719592534,0.7418050587973032,0.46153846153846156,0.06180041784216738,0.11851504520949105,101895.60372403836,193.9605838613745,20.576923076923077,4.17059,3945.7989165067675,0.8012150287628174,4223.8673583405725,0.45159866325593434,1.0,1.0,0.0,0.05688219535982175,0.06558436878663254,99491.49864758716,19.472884837488856,28.0,0,1,1,11,1299.8,52.5,54.5,0.5447227191413238,0.11949963987283921
22
+ REALMLP (default),188.56736958373307,2.8279882822281275,22.387772498457988,3.1534644940370367,0.8482043138660138,0.8308100952567204,0.0,0.07049246793231062,0.12882128240536025,856.0818546821283,166.2876065138886,21.307692307692307,4.68787,113.99487728542752,2.6188460985819497,21.860990319181454,0.8382976743420607,0.985771610248003,0.9237062317944604,0.0,0.056187515565281676,0.10042346984448043,594.293460461532,57.629925208930345,22.0,0,0,0,13,1283.5,46.1,47.1,0.5277280858676208,0.054416123092593686
23
+ XT (tuned + ensemble),1206.5194327423715,3.367576224375994,442.36567464742467,1.069316176291616,0.8375900418733949,0.8513977817862853,0.0,0.10048529388089425,0.120775550104976,8220.916376158188,88.72398178178156,21.53846153846154,5.04794,766.4287914435068,3.576531834072537,158.22496863160976,0.8436571643025992,1.0,0.9970859984141835,0.0,0.04988373521321621,0.12269401261109883,5379.1926370125775,93.77443700336585,25.0,0,0,0,13,1272.5,52.4,59.1,0.5223613595706619,0.07374831493706267
24
+ GBM (default),10.696644908750159,2.245801542559241,3.3219193518156818,0.4939224454467883,0.9087857894621156,0.8834968815958729,0.0,0.08060606825955649,0.11976658053442996,80.53396018410795,58.38494586351267,21.923076923076923,4.4838,7.6057972113291425,0.9335102770063612,2.1107352135741744,0.2745991643955073,0.9712364739383097,0.9121375216694869,0.0,0.04937573951369445,0.10636020418552539,91.35379089849596,24.071571166022103,21.0,0,0,0,13,1263.8,53.0,53.2,0.5134168157423972,0.04684267482014358
25
+ XT (tuned),1206.5194327423715,0.45855456478575357,442.36567464742467,0.18106308268317445,0.8639666647767655,0.8723069971625098,0.0,0.10266445862489976,0.12783943156365074,8220.916376158188,14.733646953552363,22.346153846153847,5.07281,766.4287914435068,0.34996385044521755,158.22496863160976,0.15116311924152026,1.0,0.9903008544022194,0.0,0.050434912392149145,0.12806468799723258,5379.1926370125775,14.85253877204164,24.0,0,0,0,13,1254.9,41.8,52.3,0.5035778175313059,0.05933488091441373
26
+ TABPFNV2_GPU (default),13.930354648573786,0.6212240172247602,5.213536664770012,0.4399683707018391,0.7617874026169423,0.7938464861305419,0.46153846153846156,0.07567956002207855,0.13811994843145634,115.71517682132266,30.565681397453538,23.26923076923077,4.25916,9.373317972819011,0.4424108028411865,2.803336175829957,0.3130195506083406,1.0,1.0,0.0,0.06281203604096552,0.07601826824112161,120.93804022420875,33.21203399192692,28.5,0,0,0,13,1231.2,58.6,60.3,0.4821109123434705,0.08671869218804537
27
+ NN_TORCH (tuned),44960.17932087947,0.2904610134597517,5714.412959426929,0.10578639607158029,0.8688481869538994,0.8668830387716895,0.0,0.0842812846349075,0.1388441315022607,220430.49479235077,7.008617614539204,23.307692307692307,4.7148,15497.21247045199,0.18749599986606175,4608.594420268999,0.09690652350320204,1.0,0.9449347108759663,0.0,0.0730528979706524,0.0987961550603162,177697.9133637009,6.6162587674624795,23.0,0,0,0,13,1230.2,46.0,47.0,0.481216457960644,0.0496801955102473
28
+ XGB (default),12.109628748486186,0.9366908014330091,3.1242101566662273,0.3058622971724495,0.8862480764613536,0.8643492470670666,0.0,0.08752819812522358,0.12572826983269936,67.76669386312491,28.981192639888032,24.115384615384617,4.75458,7.79438853263855,0.5301833947499593,2.2441525977447814,0.24247013095584213,1.0,0.9587276133688395,0.0,0.053268364359151055,0.105372572651016,75.78126190262087,16.70456778360137,24.0,0,0,0,13,1214.6,41.8,46.0,0.462432915921288,0.046556052745046386
29
+ RF (tuned + ensemble),2056.9684221919783,3.432567118171953,500.6828145780351,1.0841887922659657,0.9244962574709313,0.9210959637362763,0.0,0.10851512257257524,0.13041932100317657,10863.776799780928,90.24173062908258,24.46153846153846,5.10626,1088.11842862765,1.444333102968004,515.7302180242054,0.7709478321252661,1.0,0.9973408733427616,0.0,0.06747831829737316,0.13942046203027075,11909.387526812274,91.13733276245972,25.0,0,0,0,13,1202.6,45.8,47.1,0.4543828264758497,0.044566348818597695
30
+ RF (tuned),2056.9684221919783,0.3944415463341607,500.6828145780351,0.1540690468576434,0.9446007082199963,0.9408578430892015,0.0,0.11360740147863871,0.13890562008885568,10863.776799780928,12.055960583687282,26.23076923076923,5.18858,1088.11842862765,0.3136819733513726,515.7302180242054,0.12356183047078627,1.0,1.0,0.0,0.07199386349783587,0.14203697859305436,11909.387526812274,11.406268840053338,28.0,0,0,0,13,1152.7,48.4,57.1,0.41323792486583183,0.040812401020999874
31
+ EBM (tuned + ensemble),26125.740811508127,1.1628193525167612,3543.4601988059676,0.44619286429284205,0.8289294744561572,0.8515101676633261,0.0,0.1367713941762849,0.19454116603903474,102743.72801916403,13.991542249573758,27.0,4.37076,6865.337549757957,0.3269915845659044,1890.6770917738593,0.13287644820933492,1.0,1.0,0.0,0.1248421766855935,0.1374516591403558,100996.51953892264,10.735277156769923,33.0,0,1,0,12,1136.0,45.1,60.4,0.3953488372093023,0.07486975066160587
32
+ XT (default),4.038539225015885,0.28929371426248146,0.7743502790637721,0.08386602222003156,0.9314402600373994,0.9226381137036738,0.0,0.12238733986993501,0.17918453000563647,12.035409694284047,7.5346994725559195,28.115384615384617,5.13889,2.058894846174452,0.22108591927422416,0.46766450701756074,0.055025941487738636,1.0,1.0,0.0,0.08970042626125052,0.15512507020900368,11.279879616649893,6.868103658105229,31.0,0,0,0,13,1100.4,55.5,62.8,0.3694096601073345,0.04000377617175779
33
+ EBM (tuned),26125.740811508127,0.09807351307991222,3543.4601988059676,0.04003457596727012,0.8498896422587312,0.8690004985226352,0.0,0.14218261386610534,0.20871356711851735,102743.72801916403,1.2806753432569877,28.53846153846154,4.42885,6865.337549757957,0.03976681497361925,1890.6770917738593,0.012767925630469206,1.0,1.0,0.0,0.1292889595673019,0.16421526338237233,100996.51953892264,1.0,34.0,0,0,1,12,1091.5,49.5,56.7,0.3595706618962433,0.05876238598299918
34
+ FASTAI (tuned + ensemble),4642.340904501768,8.77000401672135,1248.4159649024482,5.455805855994076,0.9763387226193126,0.966122715762154,0.0,0.12202126425715551,0.19741406156575694,32660.757986061028,336.5557366220283,30.0,5.27232,3620.151651991738,7.070411682128906,540.0550122797715,2.672383567926809,1.0,1.0,0.0,0.1022000155980195,0.1756471698456369,33252.590479353305,324.2617231497696,31.0,0,0,0,13,1040.0,61.4,49.3,0.32558139534883723,0.0357112186773915
35
+ NN_TORCH (default),151.43337847534409,0.22200924249795764,23.930992166336175,0.08705592871966639,0.9827422496757343,0.9678685866022871,0.0,0.1193977421999948,0.19547728530185762,831.2534637345914,6.296769612194,30.23076923076923,4.81722,65.27966655625238,0.14955372280544704,20.47535029226034,0.07966387341594139,1.0,1.0,0.0,0.14050249681884142,0.13614151881375058,546.0088939544379,6.398331846186531,32.0,0,0,0,13,1038.3,57.6,62.3,0.32021466905187834,0.03550445228984282
36
+ EBM (default),61.24195487458481,0.11774356426336827,10.057660454061297,0.07098698960857609,0.8908598695337496,0.8984005515464777,0.0,0.1505943663504528,0.22072991542366915,260.56038663650384,2.782386904835081,30.615384615384617,4.44221,19.539602756500244,0.04366175333658854,6.327684720357259,0.039228283502797535,1.0,1.0,0.0,0.13598656576261503,0.17428198310764084,214.4635432482436,1.7877726532004268,35.0,0,0,0,13,1034.2,43.3,66.2,0.3112701252236136,0.036159887762040425
37
+ RF (default),9.267833071284823,0.31600932585887426,1.1524694810969494,0.08818027662752802,1.0,0.9870278022164788,0.0,0.12896216114203193,0.1901035125951938,27.793203837891944,7.952067403429296,31.346153846153847,5.26146,5.924832847383287,0.26005201869540745,0.5271703851240811,0.062155184511682476,1.0,1.0,0.0,0.08142906388988314,0.17217694118713336,20.351375021697393,6.812325017295935,31.5,0,0,0,13,1000.0,0.0,0.0,0.29427549194991054,0.03259951679594785
38
+ FASTAI (tuned),4642.340904501768,0.9693611326380673,1248.4159649024482,0.5658202849858516,0.9859539042371194,0.9812210227156647,0.0,0.1268703134805104,0.21643571616372462,32660.757986061028,38.043706128121705,31.692307692307693,5.18701,3620.151651991738,1.0039918687608507,540.0550122797715,0.32495714500374384,1.0,1.0,0.0,0.11787324661401322,0.18136473255372101,33252.590479353305,36.96325797937462,33.0,0,0,0,13,991.5,48.2,64.3,0.28622540250447226,0.03287029132617368
39
+ FASTAI (default),18.837261917856004,0.970951892168094,4.540025666114834,0.4814105255967321,1.0,0.9959412543360979,0.0,0.1713566965946914,0.27575692774796806,123.96432879696866,34.056295479841545,34.92307692307692,6.36878,15.734567880630493,1.036571078830295,2.6035035917474465,0.39168673048638547,1.0,1.0,0.0,0.11912950408368406,0.27762932440609267,138.38888737159849,36.35774230804583,36.0,0,0,0,13,858.2,52.8,69.4,0.2110912343470483,0.029119709509087932
40
+ KNN (tuned + ensemble),20.494465350493407,2.240403470422468,2.7608828307729607,0.3822937952790146,1.0,0.9953083923448788,0.07692307692307693,0.3613185363694843,0.6538216817153736,74.30220244876604,25.830539833366643,39.76923076923077,8.1617,12.562016169230143,0.2777775393591987,2.427842858706766,0.14225338857865483,1.0,1.0,0.0,0.41352430110915517,0.7463425706140449,61.12257192545374,15.449077396683121,41.0,0,0,0,13,532.3,77.3,95.3,0.09838998211091235,0.025256773577856426
41
+ LR (tuned + ensemble),260.53517347665934,0.6450919024964683,90.82426667518703,0.24319307436598073,1.0,1.0,0.0,0.35422421997100256,0.6560326250854102,2016.752248654558,10.333316070921164,40.30769230769231,8.15038,155.9093332555559,0.2263851695590549,45.737119844257606,0.10677772758861735,1.0,1.0,0.0,0.3837007856022804,0.7329121008806585,1735.7243863195374,7.455644136363483,40.0,0,0,0,13,488.7,52.5,94.2,0.08586762075134168,0.024850461752159034
42
+ KNN (tuned),20.494465350493407,0.2574708763350788,2.7608828307729607,0.05410989827741383,1.0,0.9988376690148505,0.07692307692307693,0.36756573770309675,0.6864360755861012,74.30220244876604,3.3744578525576268,40.76923076923077,8.25638,12.562016169230143,0.09626038869222005,2.427842858706766,0.028318042556444805,1.0,1.0,0.0,0.42023780750084094,0.7514125894823619,61.12257192545374,2.1032822899149863,42.0,0,0,0,13,441.9,67.3,134.2,0.07513416815742398,0.024635899785160127
43
+ LR (tuned),260.53517347665934,0.13893676680377404,90.82426667518703,0.0633906225067962,1.0,1.0,0.0,0.3558634149624985,0.6630785837896581,2016.752248654558,3.076869056672641,40.92307692307692,8.15927,155.9093332555559,0.05223793453640408,45.737119844257606,0.05009355954825878,1.0,1.0,0.0,0.38692302721983185,0.7430806596590566,1735.7243863195374,2.8910146707817423,41.0,0,0,0,13,420.6,81.6,94.5,0.07155635062611806,0.02449625775032643
44
+ LR (default),4.903454369968838,0.19863318235446245,2.033074367074023,0.07563726553282422,1.0,1.0,0.0,0.3808091655367071,0.7702257899846487,45.803264898726965,4.508173919929611,42.15384615384615,8.22758,5.286295996771918,0.09712121221754286,1.191311693685635,0.0859815087197251,1.0,1.0,0.0,0.38694683448707623,0.8770019722050186,34.79737790723628,4.100500531123858,42.0,0,0,0,13,290.1,86.6,112.6,0.04293381037567084,0.023757512031433232
45
+ KNN (default),2.0401845610039864,0.07292802802517882,0.5185761600539422,0.029646415007926124,1.0,0.999847626563869,0.07692307692307693,0.4076491058824559,0.8492569035691879,1.1215157323318734,1.57971988992164,42.61538461538461,8.55152,0.15191650390625,0.04789047771030002,0.03752343922831045,0.021942545900661376,1.0,1.0,0.0,0.4573857803481276,1.0,1.0,1.5317182357480712,44.0,0,0,0,13,238.6,87.0,145.9,0.03220035778175313,0.023578620171993768
data/{imputation_no/splits_all/tasks_multiclass/datasets_medium/tuning-impact-elo.png.zip → full-imputed-reg/time_plot.png.zip} RENAMED
@@ -1,3 +1,3 @@
1
  version https://git-lfs.github.com/spec/v1
2
- oid sha256:d7c42b2b29de41b52b524dba9697dd3104494687f70d58e6d33189bf7d733c4a
3
- size 99826
 
1
  version https://git-lfs.github.com/spec/v1
2
+ oid sha256:da71d8796986e598f595ef583a35349dd78b721b6d2ad86658f6df61f9e4e254
3
+ size 84316
data/full-imputed/leaderboard.tex ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ \begin{tabular}{llcccccrr}
2
+ \toprule
3
+ \textbf{Model} & \textbf{Elo ($\uparrow$)} & \textbf{Norm.} & \textbf{Avg.} & \textbf{Harm.} & \textbf{\#wins ($\uparrow$)} & \textbf{Improva-} & \textbf{Train time} & \textbf{Predict time} \\
4
+ & & \textbf{score ($\uparrow$)} & \textbf{rank ($\downarrow$)} & \textbf{mean} & & \textbf{bility ($\downarrow$)} & \textbf{per 1K [s]} & \textbf{per 1K [s]} \\
5
+ & & & & \textbf{rank ($\downarrow$)} & & & & \\
6
+ \midrule
7
+ AutoGluon 1.3 (4h) & \textcolor{gold}{\textbf{1590${}_{-28,+24}$}} & \textcolor{gold}{\textbf{0.618}} & \textcolor{gold}{\textbf{8.4}} & \textcolor{gold}{\textbf{3.1}} & \textcolor{silver}{\textbf{8}} & \textcolor{gold}{\textbf{5.8\%}} & 1408.78 & 3.34 \\
8
+ RealMLP (T+E) & \textcolor{silver}{\textbf{1565${}_{-23,+28}$}} & \textcolor{silver}{\textbf{0.537}} & \textcolor{silver}{\textbf{9.1}} & 5.7 & 0 & \textcolor{silver}{\textbf{6.6\%}} & 6564.71 & 10.26 \\
9
+ TabM (T+E) & \textcolor{bronze}{\textbf{1544${}_{-22,+27}$}} & \textcolor{bronze}{\textbf{0.502}} & \textcolor{bronze}{\textbf{9.9}} & 4.9 & 3 & \textcolor{bronze}{\textbf{7.0\%}} & 3285.87 & 1.47 \\
10
+ LightGBM (T+E) & 1526${}_{-24,+24}$ & 0.452 & 10.6 & 6.6 & 1 & 8.6\% & 416.98 & 2.64 \\
11
+ CatBoost (T+E) & 1484${}_{-26,+19}$ & 0.433 & 12.4 & 8.2 & 0 & 7.9\% & 1658.41 & 0.65 \\
12
+ CatBoost (T) & 1470${}_{-23,+18}$ & 0.416 & 13.0 & 7.0 & 1 & 8.1\% & 1658.41 & 0.08 \\
13
+ TabM (T) & 1453${}_{-27,+20}$ & 0.413 & 13.7 & 7.2 & 1 & 8.0\% & 3285.87 & 0.17 \\
14
+ LightGBM (T) & 1448${}_{-27,+23}$ & 0.363 & 14.0 & 11.7 & 0 & 9.3\% & 416.98 & 0.33 \\
15
+ XGBoost (T+E) & 1439${}_{-20,+21}$ & 0.361 & 14.3 & 9.9 & 0 & 9.4\% & 693.49 & 1.69 \\
16
+ ModernNCA (T+E) & 1434${}_{-27,+24}$ & 0.444 & 14.5 & 5.3 & 3 & 8.7\% & 4621.67 & 8.15 \\
17
+ CatBoost (D) & 1428${}_{-20,+27}$ & 0.372 & 14.8 & 7.9 & 1 & 9.3\% & 6.83 & 0.08 \\
18
+ TabPFNv2 (T+E) & 1415${}_{-26,+27}$ & 0.476 & 15.4 & \textcolor{gold}{\textbf{3.1}} & \textcolor{gold}{\textbf{11}} & 8.5\% & 3030.15 & 21.44 \\
19
+ XGBoost (T) & 1405${}_{-23,+22}$ & 0.313 & 15.9 & 13.2 & 0 & 9.7\% & 693.49 & 0.31 \\
20
+ ModernNCA (T) & 1402${}_{-21,+18}$ & 0.307 & 16.0 & 8.9 & 1 & 9.3\% & 4621.67 & 0.47 \\
21
+ TabICL (D) & 1390${}_{-24,+24}$ & 0.381 & 16.6 & \textcolor{bronze}{\textbf{4.5}} & 6 & 9.2\% & 6.63 & 1.48 \\
22
+ TabPFNv2 (T) & 1348${}_{-24,+24}$ & 0.360 & 18.6 & 5.7 & 1 & 10.6\% & 3030.15 & 0.46 \\
23
+ RealMLP (T) & 1348${}_{-21,+20}$ & 0.229 & 18.6 & 15.9 & 0 & 10.1\% & 6564.71 & 0.49 \\
24
+ TabM (D) & 1347${}_{-19,+20}$ & 0.285 & 18.6 & 12.6 & 0 & 10.9\% & 10.49 & 0.13 \\
25
+ TorchMLP (T+E) & 1332${}_{-21,+25}$ & 0.220 & 19.4 & 14.8 & 0 & 10.6\% & 2874.67 & 1.95 \\
26
+ TabPFNv2 (D) & 1321${}_{-23,+21}$ & 0.324 & 20.0 & 5.6 & 4 & 11.6\% & 3.36 & 0.31 \\
27
+ TabDPT (D) & 1300${}_{-28,+25}$ & 0.300 & 21.0 & 4.9 & \textcolor{bronze}{\textbf{7}} & 12.3\% & 22.53 & 8.55 \\
28
+ ModernNCA (D) & 1299${}_{-23,+25}$ & 0.159 & 21.0 & 12.8 & 1 & 12.8\% & 14.87 & 0.31 \\
29
+ EBM (T+E) & 1292${}_{-25,+24}$ & 0.184 & 21.4 & 13.5 & 0 & 14.6\% & 1331.68 & 0.20 \\
30
+ FastaiMLP (T+E) & 1250${}_{-19,+24}$ & 0.158 & 23.5 & 14.5 & 0 & 14.0\% & 593.24 & 4.47 \\
31
+ RealMLP (D) & 1244${}_{-25,+21}$ & 0.104 & 23.7 & 20.3 & 0 & 12.7\% & 21.86 & 0.84 \\
32
+ ExtraTrees (T+E) & 1243${}_{-21,+22}$ & 0.124 & 23.8 & 15.8 & 0 & 14.3\% & 183.02 & 0.76 \\
33
+ EBM (T) & 1236${}_{-21,+18}$ & 0.135 & 24.1 & 17.7 & 0 & 15.3\% & 1331.68 & 0.02 \\
34
+ XGBoost (D) & 1232${}_{-24,+19}$ & 0.115 & 24.4 & 19.8 & 0 & 12.7\% & 1.94 & 0.12 \\
35
+ TorchMLP (T) & 1223${}_{-21,+25}$ & 0.113 & 24.8 & 21.3 & 0 & 12.7\% & 2874.67 & 0.13 \\
36
+ EBM (D) & 1202${}_{-29,+22}$ & 0.134 & 25.7 & 13.5 & 1 & 16.2\% & 4.67 & 0.04 \\
37
+ LightGBM (D) & 1202${}_{-23,+23}$ & 0.088 & 25.7 & 23.0 & 0 & 13.5\% & 1.96 & 0.14 \\
38
+ RandomForest (T+E) & 1201${}_{-24,+21}$ & 0.098 & 25.8 & 15.9 & 0 & 15.1\% & 373.18 & 0.77 \\
39
+ ExtraTrees (T) & 1200${}_{-28,+18}$ & 0.094 & 25.9 & 17.1 & 0 & 15.4\% & 183.02 & 0.09 \\
40
+ FastaiMLP (T) & 1161${}_{-19,+24}$ & 0.073 & 27.7 & 22.6 & 0 & 15.6\% & 593.24 & 0.31 \\
41
+ RandomForest (T) & 1152${}_{-25,+19}$ & 0.072 & 28.1 & 18.0 & 0 & 16.2\% & 373.18 & 0.09 \\
42
+ TorchMLP (D) & 1066${}_{-21,+24}$ & 0.022 & 31.6 & 29.0 & 0 & 17.5\% & 9.99 & 0.13 \\
43
+ FastaiMLP (D) & 1011${}_{-23,+23}$ & 0.023 & 33.7 & 31.0 & 0 & 20.8\% & 2.86 & 0.37 \\
44
+ RandomForest (D) & 1000${}_{-0,+0}$ & 0.009 & 34.0 & 32.4 & 0 & 21.3\% & 0.43 & 0.05 \\
45
+ ExtraTrees (D) & 972${}_{-28,+21}$ & 0.025 & 35.0 & 31.4 & 0 & 23.0\% & 0.25 & 0.05 \\
46
+ Linear (T+E) & 917${}_{-21,+31}$ & 0.031 & 36.6 & 28.3 & 0 & 30.8\% & 47.49 & 0.17 \\
47
+ Linear (T) & 882${}_{-30,+32}$ & 0.020 & 37.5 & 32.8 & 0 & 31.5\% & 47.49 & 0.07 \\
48
+ Linear (D) & 862${}_{-30,+23}$ & 0.014 & 38.1 & 30.5 & 0 & 32.9\% & 1.52 & 0.09 \\
49
+ KNN (T+E) & 685${}_{-32,+27}$ & 0.000 & 41.4 & 41.1 & 0 & 45.3\% & 2.74 & 0.18 \\
50
+ KNN (T) & 608${}_{-48,+32}$ & 0.000 & 42.4 & 42.3 & 0 & 46.9\% & 2.74 & 0.04 \\
51
+ KNN (D) & 459${}_{-45,+42}$ & 0.000 & 43.8 & 43.6 & 0 & 54.1\% & 0.05 & 0.02 \\
52
+ \bottomrule
53
+ \end{tabular}
data/full-imputed/tabarena_leaderboard.csv ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ method,time_train_s,time_infer_s,time_train_s_per_1K,time_infer_s_per_1K,normalized-error,normalized-error-task,imputed,champ_delta,loss_rescaled,time_train_s_rescaled,time_infer_s_rescaled,rank,median_metric_error,median_time_train_s,median_time_infer_s,median_time_train_s_per_1K,median_time_infer_s_per_1K,median_normalized-error,median_normalized-error-task,median_imputed,median_champ_delta,median_loss_rescaled,median_time_train_s_rescaled,median_time_infer_s_rescaled,median_rank,rank=1_count,rank=2_count,rank=3_count,rank>3_count,elo,elo+,elo-,winrate,mrr
2
+ AutoGluon 1.3 (4h),8462.98349033711,33.138286938542635,2912.1242917208747,4.391016265244401,0.381932874173557,0.3938004850803849,0.0,0.05822662138626224,0.038656168015376684,43301.56973340033,539.3969074295995,8.411764705882353,0.2055,7367.614226023356,3.9490213659074573,1408.7828331379249,3.337414261487274,0.32575782629688715,0.34354944358388295,0.0,0.022224886416280176,0.016643068102528722,33121.993698706065,185.27028732280235,5.0,8,6,2,35,1589.7,23.3,27.4,0.8315508021390374,0.32434985005444883
3
+ REALMLP (tuned + ensemble),82918.85938235045,50.75538799222518,8669.653795437369,17.385570695148495,0.4634803896199485,0.48980490794993603,0.0,0.0662652507057572,0.04428998923773407,179788.549283492,846.8926595159473,9.147058823529411,0.20859,30826.14291326205,23.329282654656303,6564.713231881598,10.263652729721208,0.4233707478718706,0.4394625063276673,0.0,0.03156540515236528,0.021508501844188344,136595.8709190495,808.3809230744637,7.0,0,5,4,42,1564.6,27.5,23.0,0.8148395721925134,0.17688848740169394
4
+ TABM_GPU (tuned + ensemble),36462.58337058756,8.791583087761158,5017.258422060137,3.1802055454964386,0.49791774396870153,0.5234045251267629,0.0,0.06977046437303486,0.06341802359213981,80230.4785975727,130.9039885664859,9.872549019607844,0.20535,8420.993191123009,2.7141049438052707,3285.8688373170553,1.4723486146222118,0.4651893659350262,0.5081840173827434,0.0,0.03250086419089859,0.027567784172117953,47614.825535817596,111.53030159160512,8.0,3,2,3,43,1543.9,26.8,21.9,0.7983511586452763,0.20599586481497986
5
+ GBM (tuned + ensemble),3088.2834950947295,22.058380506500956,771.2925090199282,4.080656504886727,0.5482943614672702,0.5697587102764388,0.0,0.08557614755706973,0.059109015015710746,11617.562781863631,427.3461655076801,10.627450980392156,0.2112,1667.667911251386,3.7860276963975696,416.9832926671224,2.6387318875879693,0.5661942244497019,0.5831585230842022,0.0,0.04023364809387353,0.02358455413295459,9501.568897853978,118.5632719261967,10.0,1,1,2,47,1526.2,23.7,23.4,0.7811942959001783,0.1520008705225847
6
+ CAT (tuned + ensemble),20021.3498029566,3.148214934334516,4130.291158755297,0.9792413821552454,0.5665168479793786,0.5762538702782276,0.0,0.07932910031410541,0.05277138538144033,58497.64726501732,62.60491239485109,12.372549019607844,0.21079,7127.110804247856,1.3727677133348253,1658.412974283854,0.65278892715772,0.5695869531891277,0.5907800269434179,0.0,0.04606752490069621,0.02774139238223889,22349.13952253535,41.43902270876451,11.0,0,1,4,46,1484.5,18.5,25.4,0.7415329768270945,0.12146805837673025
7
+ CAT (tuned),20021.3498029566,0.45918073721700764,4130.291158755297,0.13269281663977184,0.5837954660770609,0.5897551898062613,0.0,0.08123455741105615,0.05345084183416724,58497.64726501732,8.058537488270943,12.96078431372549,0.21123,7127.110804247856,0.16668947537740073,1658.412974283854,0.08101450844552308,0.5845247966032667,0.6225038615712537,0.0,0.05045951030687079,0.030933776248240515,22349.13952253535,5.582269457200397,12.5,1,3,2,45,1470.5,17.3,22.3,0.7281639928698752,0.1437603439953039
8
+ TABM_GPU (tuned),36462.58337058756,0.9691277276976176,5017.258422060137,0.3543499279245272,0.5874385027995309,0.5895097011198668,0.0,0.08005397326560945,0.07918093169544495,80230.4785975727,13.69951936973594,13.696078431372548,0.21042,8420.993191123009,0.2791590425703261,3285.8688373170553,0.1728818165133111,0.559085035183601,0.6013449948327668,0.0,0.041022605221466724,0.035365730806074164,47614.825535817596,11.564361688735561,14.0,1,3,2,45,1452.9,20.0,26.7,0.7114527629233511,0.1390380052974814
9
+ GBM (tuned),3088.2834950947295,3.280151636356362,771.2925090199282,0.7008989122586311,0.6366067117608026,0.6336828329141213,0.0,0.09282220252278703,0.0695723239257395,11617.562781863631,67.66975356224859,13.950980392156863,0.21181,1667.667911251386,0.5984517203436958,416.9832926671224,0.33384935590955944,0.6296869528283883,0.6590584882044693,0.0,0.05048061255226499,0.03622173132751954,9501.568897853978,17.561755180687065,12.0,0,0,0,51,1447.5,22.7,26.1,0.7056595365418895,0.08527400346153444
10
+ XGB (tuned + ensemble),6066.175009788823,8.000445660682546,1229.0991373608288,3.120951906510457,0.6385613101625683,0.6434959772946812,0.0,0.09352817276204972,0.06895162562612145,14800.692905333448,192.56405868832437,14.27450980392157,0.21458,2256.9276883072325,3.0598979949951173,693.4907982506384,1.6904262577061315,0.671301017368657,0.6581535714975986,0.0,0.05552089944600691,0.03200108485206017,11769.825378159274,82.2877578838671,13.0,0,1,1,49,1438.7,20.3,19.4,0.698306595365419,0.1010413685016667
11
+ MNCA_GPU (tuned + ensemble),50831.51192726024,408.7024568246081,6035.4844365627405,41.810485127597225,0.5559334685448146,0.5505192494459418,0.0,0.08712344899479532,0.06897276604102788,107380.48324313454,2632.7754273052897,14.549019607843137,0.21294,14486.050127214856,13.240725604693095,4621.665633563503,8.148513113458952,0.5384753932635177,0.5315179526728905,0.0,0.035276378418488075,0.031199094919591976,89084.88026764008,537.8274695033359,11.0,3,3,5,40,1433.8,23.6,26.7,0.6920677361853832,0.1903874062097513
12
+ CAT (default),190.03885268302784,0.2709793741147243,87.82759080296049,0.13562630112555335,0.6279909747535407,0.646125708691512,0.0,0.0925312918791838,0.06081652619205146,428.26912869207973,7.186903437353133,14.794117647058824,0.21142,28.268621895048355,0.1854714552561442,6.827020885706225,0.08026752106160412,0.6534673037587888,0.6439696146756235,0.0,0.04582469430261593,0.029179417473348953,113.47556087857672,6.139565341592893,16.0,1,3,1,46,1428.5,26.2,19.6,0.6864973262032086,0.12705337781476866
13
+ TABPFNV2_GPU (tuned + ensemble),9338.42554130284,95.46951768190513,2848.5135701006398,47.682041415407696,0.5239705071709494,0.5769312699511674,0.35294117647058826,0.08515142798212334,0.08426448408310144,62904.17752069347,3721.414760799794,15.372549019607844,0.22709,2098.550690642993,13.798940539360046,3030.145221648074,21.443889733771147,0.5915448073500406,0.6069332103645363,0.0,0.03866296841083827,0.03250100481138207,29840.56658077224,861.272449686595,11.0,11,4,3,33,1414.7,26.3,25.1,0.6733511586452763,0.3218844858197347
14
+ XGB (tuned),6066.175009788823,1.6190793797341305,1229.0991373608288,0.7004875821871327,0.6869435084156271,0.6795624628612148,0.0,0.09696298272522294,0.07331091441041244,14800.692905333448,40.17091923504828,15.892156862745098,0.21499,2256.9276883072325,0.41774741808573407,693.4907982506384,0.3083513292273418,0.7193303230280006,0.7149553857561783,0.0,0.06100086263844462,0.03462012868860662,11769.825378159274,12.136706817415504,15.0,0,0,0,51,1404.9,21.4,22.2,0.6615418894830659,0.07556633120131231
15
+ MNCA_GPU (tuned),50831.51192726024,15.584971993005873,6035.4844365627405,1.7226505575752833,0.692913423977365,0.6430754651403978,0.0,0.09326988355673647,0.07237812189150603,107380.48324313454,104.77227719302421,16.03921568627451,0.21328,14486.050127214856,0.585451708899604,4621.665633563503,0.4747242314576746,0.7553848478357736,0.6366009339265687,0.0,0.06113391902932641,0.0493534926522164,89084.88026764008,26.6476237825924,16.0,1,1,2,47,1402.5,17.6,20.6,0.6581996434937611,0.1120344583371438
16
+ TABICL_GPU (default),82.80334805787777,14.446106202929627,7.4655941033985815,1.6847655412525513,0.6187626573361198,0.6590285685589051,0.29411764705882354,0.09205009921867735,0.09066558702327601,132.83095048510535,173.47404322746496,16.607843137254903,0.21659,20.0619904200236,1.316457470258077,6.625686376434735,1.479660415649414,0.6419318555001297,0.737430199336512,0.0,0.05183434289459943,0.0307061823959099,110.37274820175764,91.25701004379168,14.0,6,4,1,40,1389.8,23.7,23.2,0.6452762923351159,0.22043663312484155
17
+ REALMLP (tuned),82918.85938235045,2.265432894048088,8669.653795437369,0.9268647174900084,0.770575243056979,0.7198426366318216,0.0,0.10098849134105925,0.08466680664165582,179788.549283492,40.77005886388886,18.61764705882353,0.21508,30826.14291326205,1.0006839964124892,6564.713231881598,0.487512478277248,0.866614722551564,0.7557285234401128,0.0,0.06823335132903552,0.05320262249246514,136595.8709190495,36.173930978956705,18.0,0,0,0,51,1347.8,19.9,20.3,0.5995989304812834,0.06290892887580088
18
+ TABPFNV2_GPU (tuned),9338.42554130284,3.508518432739773,2848.5135701006398,1.6633483351042642,0.6402449044416614,0.6630497140643348,0.35294117647058826,0.10613854546664524,0.10151316508024909,62904.17752069347,134.3876978041907,18.61764705882353,0.22751,2098.550690642993,0.5097733656565349,3030.145221648074,0.46198440414964803,0.7603547647708915,0.7104310097848833,0.0,0.07667977096051137,0.04654872982993791,29840.56658077224,22.48627830890164,15.5,1,9,2,39,1348.2,23.9,23.8,0.5995989304812834,0.1748060544077359
19
+ TABM_GPU (default),139.62633961878052,1.0737770921524312,22.4928837749369,0.40818286049864,0.7149663388553331,0.7265875725216725,0.0,0.10904673882873887,0.10206768551470595,289.28104127940037,12.833742622244758,18.61764705882353,0.21188,40.332407061258955,0.18133597903781468,10.492230631952996,0.13221126395693922,0.8197432069948619,0.7908796299405498,0.0,0.05780353607735633,0.03944837820705307,186.77830789084513,10.724711035853641,17.0,0,0,1,50,1346.8,19.5,18.3,0.5995989304812834,0.07920722957248505
20
+ NN_TORCH (tuned + ensemble),29590.123960411,13.183612702147375,3729.842332819238,3.5618312259496654,0.7804812465107793,0.7650139226387963,0.0,0.1061112883342963,0.09038128962619164,97942.50427646744,194.194370742189,19.392156862745097,0.21479,10848.660208092795,3.946354971991645,2874.6743506773596,1.9516254299583915,0.910350140134773,0.8406524459297455,0.0,0.06624137678637454,0.057854463852261596,61441.834728019065,144.9992483814283,20.0,0,0,0,51,1332.3,24.5,20.5,0.5819964349376114,0.06762812359455618
21
+ TABPFNV2_GPU (default),12.045264010803372,0.825882379421741,4.478694030451919,0.4530850283232118,0.6758133693268394,0.7167075967625335,0.35294117647058826,0.11639838128335792,0.11295068368765535,69.53545044274777,29.75030650326298,20.04901960784314,0.22887,9.117408725950453,0.42119165261586505,3.3572004182270923,0.3130195506083406,0.956340758739766,0.8514943913344442,0.0,0.07592598037026033,0.052795487060598015,52.331266670190345,18.512009051191257,21.0,4,1,4,42,1320.6,20.6,22.2,0.5670677361853832,0.1788864671189465
22
+ TABDPT_GPU (default),166.18249968905855,63.11337411850366,27.980557312145997,23.24231264879125,0.6999265055194402,0.699617586999111,0.0,0.12276620509418873,0.09536847721359762,577.6877537613715,1486.1376930964866,20.96078431372549,0.22562,99.10453534126282,28.39870807859633,22.52690614988171,8.550738306685618,0.9903591350487536,0.8691048403402053,0.0,0.05029899916003,0.0433680924463882,528.528670151851,1255.434427440434,22.0,7,0,3,41,1300.5,24.7,27.1,0.5463458110516934,0.2050929674182998
23
+ MNCA_GPU (default),252.05720278889527,8.333707038145958,17.796029105655933,1.1526772129094414,0.840779547661087,0.7959093866791253,0.0,0.12833371899167995,0.09667612599337075,329.3551130182264,62.49957238602975,21.03921568627451,0.21885,36.0034454398685,0.5316168732113309,14.869495839530392,0.30768591310277943,1.0,0.8857694762766525,0.0,0.07424254289688792,0.06351531911154724,236.99495784925895,20.43419277465042,23.0,1,0,0,50,1299.1,24.9,22.8,0.5445632798573975,0.07840885625256006
24
+ EBM (tuned + ensemble),34026.536489860475,1.2927426090946903,5478.959788432886,0.48902813323980865,0.8160130792555895,0.8175846459536137,0.0,0.1459291690476727,0.13123283756498888,45019.152540237825,18.109773959357398,21.431372549019606,0.21615,2925.6548613442314,0.400875727335612,1331.6775166450918,0.19908260374566636,0.958308811651104,0.8761716756909942,0.0,0.08565806627599937,0.05590445639895017,17751.99098903195,11.13922354039944,21.0,0,1,1,49,1292.5,23.9,24.9,0.535650623885918,0.07406174849391879
25
+ FASTAI (tuned + ensemble),6629.648996400055,16.16286987151975,1343.5604508466745,7.704229376998794,0.8424987698640145,0.8369589067092069,0.0,0.13971168994404898,0.11512340310437526,22455.399300542234,425.24771894393024,23.46078431372549,0.21679,3182.895098288854,10.795994228786892,593.237788402893,4.466873745216533,1.0,0.9552259937574975,0.0,0.0932055660765102,0.07243348401502496,19851.125229101002,409.94392325616803,25.0,0,1,0,50,1249.7,24.0,19.0,0.48952762923351156,0.06883106383498735
26
+ REALMLP (default),273.729912211121,2.9575047363123343,25.2860618687397,2.9321723489284044,0.8959152468625471,0.8483293899440195,0.0,0.1265509832451891,0.10892646502099379,570.5207778272487,128.70805989538366,23.745098039215687,0.21516,107.08700556225247,2.6188460985819497,21.860990319181454,0.8382976743420607,1.0,0.8905283644114734,0.0,0.09824845633171009,0.07444845646092337,497.27886689729087,54.5386788723178,24.0,0,0,0,51,1244.4,20.7,24.5,0.483065953654189,0.049206352618688116
27
+ XT (tuned + ensemble),1289.1519403179227,3.0578905248434194,464.9314949446578,1.2901947647379284,0.8763124357874087,0.8533692619097849,0.0,0.14328509610809964,0.11780722719308848,5501.829777288316,78.94715148525705,23.823529411764707,0.21814,763.5855970117781,1.8364481396145291,183.01944048073585,0.761281055543471,1.0,0.9521925032545069,0.0,0.08603990026311015,0.07137952858629182,3537.1622158448404,68.20231667792302,27.0,0,0,1,50,1243.2,21.4,20.4,0.48128342245989303,0.06310725111805937
28
+ EBM (tuned),34026.536489860475,0.16189759790507796,5478.959788432886,0.07139558038617727,0.8649702054759879,0.8501629933938847,0.0,0.15261119112333235,0.14046524715764735,45019.152540237825,2.1906554232861546,24.11764705882353,0.21772,2925.6548613442314,0.0440410852432251,1331.6775166450918,0.022730636596679687,1.0,0.9362089365721089,0.0,0.09714090003058773,0.06554952824985644,17751.99098903195,1.2223545537568268,24.0,0,0,1,50,1236.3,17.4,20.5,0.47459893048128343,0.056429653896796474
29
+ XGB (default),12.860019501050314,0.6666093941607507,3.1825529067488505,0.3000924886961127,0.8848348696110224,0.853007192078055,0.0,0.12712004260212578,0.11901448944644484,40.716829559380564,17.42803351650834,24.441176470588236,0.21667,5.82192047437032,0.3283502260843913,1.9409389396340444,0.12262328807300621,1.0,0.9508124068469325,0.0,0.09769803191981652,0.0669685923578734,34.48133232859512,10.22021081972956,24.0,0,0,0,51,1232.4,18.8,23.5,0.4672459893048128,0.05049212098034601
30
+ NN_TORCH (tuned),29590.123960411,0.7234875786018787,3729.842332819238,0.20874333848987678,0.8872281679035978,0.8461065690351314,0.0,0.1266933814263632,0.11368972667898117,97942.50427646744,10.830632864633353,24.754901960784313,0.21784,10848.660208092795,0.2631075382232666,2874.6743506773596,0.131112832826372,1.0,0.9115838155711167,0.0,0.1030878662833985,0.06896829387646289,61441.834728019065,8.236695976463633,25.0,0,0,0,51,1223.2,25.0,20.4,0.46011586452762926,0.04694203272118342
31
+ EBM (default),104.78526760316363,0.17165232525412033,11.079598604500092,0.09398863718788793,0.8658153943709024,0.8643480238945523,0.0,0.1621049398152807,0.1485076759316338,148.0901517975655,3.2508346897376326,25.745098039215687,0.21677,11.465454594294231,0.05977429548899333,4.6738599788414605,0.03961948198354664,1.0,0.9628721496233147,0.0,0.10271933543623024,0.06419214710978799,75.98904610132907,2.400460311914132,26.0,1,0,2,48,1202.5,21.1,28.4,0.4376114081996435,0.07434503258700696
32
+ GBM (default),8.619418829147072,1.0011564760166576,3.0462908189340183,0.25265600996978405,0.9115791356403461,0.8832381096058513,0.0,0.13512306737563554,0.11629352319961023,44.26353412937288,22.939896191571705,25.745098039215687,0.21706,5.971681065029568,0.2858244842953152,1.9600326879612946,0.14173548842042513,1.0,0.9463131041198098,0.0,0.09851412306889507,0.07460814115278391,32.72231234173676,8.612355874421967,26.0,0,0,0,51,1202.3,22.3,22.7,0.4376114081996435,0.04352354270813194
33
+ RF (tuned + ensemble),2245.014868743487,2.632504310950734,530.9489806737863,1.2198476221584795,0.9024507372146047,0.8782930186177115,0.0,0.15120883991739936,0.12864400480448673,6771.2411119904955,73.51636782881279,25.84313725490196,0.21966,886.9249708387587,1.8479143513573542,373.17861356387994,0.7709478321252661,1.0,0.9768978814786328,0.0,0.0916564450840821,0.08516072052322858,5833.628398157948,63.708677480395814,28.0,0,1,1,49,1200.7,20.1,23.2,0.4353832442067736,0.06295017478288799
34
+ XT (tuned),1289.1519403179227,0.3436224609158917,464.9314949446578,0.1744826726067124,0.9058524786574399,0.8823009390085037,0.0,0.15403365971080407,0.127422275587505,5501.829777288316,9.97674146406182,25.92156862745098,0.21915,763.5855970117781,0.19129647148980033,183.01944048073585,0.09120693679998855,1.0,0.970701256454361,0.0,0.10343347628405763,0.07994728986866971,3537.1622158448404,8.867381424714122,29.0,0,1,0,50,1199.9,17.5,28.0,0.4336007130124777,0.05853966620405146
35
+ FASTAI (tuned),6629.648996400055,1.0291698354002177,1343.5604508466745,0.6091231343301344,0.9268828889794378,0.8920727938696232,0.0,0.1558685523503756,0.1360312984711962,22455.399300542234,33.54113778341792,27.65686274509804,0.2173,3182.895098288854,0.8112125396728516,593.237788402893,0.306391541190021,1.0,0.9746050760858544,0.0,0.0988046136347035,0.08330206169020851,19851.125229101002,31.29559841059079,28.0,0,0,0,51,1160.6,23.5,18.9,0.39416221033868093,0.04418776652877106
36
+ RF (tuned),2245.014868743487,0.2778017885544721,530.9489806737863,0.15585979099514927,0.9281198477241044,0.9030193429154391,0.0,0.1615360365144191,0.13978570251749914,6771.2411119904955,8.469005968793565,28.137254901960784,0.22016,886.9249708387587,0.17402595943874782,373.17861356387994,0.08526202343425164,1.0,0.9971929103870871,0.0,0.10456209496862212,0.09283342230804408,5833.628398157948,7.94881742200239,31.0,0,1,1,49,1151.9,18.9,24.1,0.38324420677361853,0.055631086241924914
37
+ NN_TORCH (default),74.85835825680128,0.5412627521423472,14.695998951842784,0.19921856017345402,0.978335352631338,0.9550533922310487,0.0,0.17458090781576796,0.15954937461607677,327.51821759182326,9.71084645377221,31.637254901960784,0.22759,34.192402362823486,0.22645958264668783,9.990997226772679,0.1258046787872722,1.0,1.0,0.0,0.14050249681884142,0.10372091439545034,204.15320945265722,7.844934898940183,33.0,0,0,0,51,1065.7,23.7,20.8,0.303698752228164,0.03443490177911753
38
+ FASTAI (default),27.993367113578813,1.070063710732138,4.951242935164595,0.5056871108143457,0.9771222767390375,0.9555114040073619,0.0,0.2080437804427664,0.19756225211381712,87.08936574190912,29.523324076097715,33.745098039215684,0.24127,12.973222759034899,0.8480017715030246,2.8561126039251374,0.37317813888351864,1.0,1.0,0.0,0.15801035292114685,0.14150143443329027,77.36570184770021,28.70383139894662,36.0,0,0,0,51,1010.9,22.5,22.1,0.2557932263814617,0.03221137350683143
39
+ RF (default),5.289521401640116,0.18747803105248345,0.8914167974682212,0.07557012377860906,0.9905963640075596,0.9728548321405758,0.0,0.2127887009205986,0.22057611136662575,11.63160057311077,4.814878723410724,34.049019607843135,0.2484,1.2628253036075168,0.08775801128811306,0.43425701731008426,0.05354175385774865,1.0,1.0,0.0,0.15453679020731148,0.13517433236964935,7.010079512208331,3.9076874559468244,35.5,0,0,0,51,1000.0,0.0,0.0,0.24888591800356505,0.03085450482928122
40
+ XT (default),3.067954011524425,0.20851136391458946,0.7599985259503769,0.07668050932017083,0.9748030390364969,0.9607261977937244,0.0,0.22983038868386624,0.2401119926385584,6.922794409042171,5.082769758447109,34.990196078431374,0.24428,1.029017792807685,0.0932659043206109,0.2473449527431567,0.04980480471851431,1.0,1.0,0.0,0.17836788960429084,0.15384123117868195,5.707021421143385,4.456198846093199,38.0,0,0,0,51,972.1,20.7,27.8,0.22749554367201427,0.03187662324031601
41
+ LR (tuned + ensemble),298.0772925112502,1.5473658189794337,106.92655310814473,0.5272909274052285,0.968958377581858,0.9646098555228514,0.0,0.3076836862047438,0.36388884495507595,1326.7185005565484,20.631703511708864,36.568627450980394,0.25172,171.24826147821216,0.28876688745286727,47.49214683366935,0.1676693138737952,1.0,1.0,0.0,0.24584842785173022,0.2585604322609332,1064.131515124883,12.175213508812917,40.0,0,0,1,50,917.3,30.7,20.1,0.19162210338680927,0.03539216425693013
42
+ LR (tuned),298.0772925112502,0.41952294077488855,106.92655310814473,0.14529639602271893,0.9796785843199748,0.970169924696589,0.0,0.31502751681846614,0.3731108004113419,1326.7185005565484,5.927745129039777,37.549019607843135,0.25255,171.24826147821216,0.10993631680806477,47.49214683366935,0.0665447885938415,1.0,1.0,0.0,0.2610498572359117,0.2575122541077485,1064.131515124883,4.067640041721783,41.0,0,0,0,51,881.6,31.6,29.4,0.16934046345811052,0.03046989425942536
43
+ LR (default),6.912347784956555,0.4444732105550163,2.521765696020704,0.1600876370747322,0.9859350644688389,0.9744519582521974,0.0,0.3287432608233925,0.4183743470806094,32.35021650084229,7.018103298699637,38.07843137254902,0.2546,5.298751910527547,0.12218634287516277,1.5162047581506444,0.0887949061495271,1.0,1.0,0.0,0.26947668808304803,0.29385559917332205,22.991652687961256,4.725399415979996,41.5,0,0,1,50,862.4,22.1,29.5,0.1573083778966132,0.03274333914572857
44
+ KNN (tuned + ensemble),129.6893764515588,9.265668427242952,9.839359763162042,0.6763553759282402,1.0,0.9960130371949129,0.11764705882352941,0.45319635453542406,0.6115009863532073,72.94693370250576,64.66285269534394,41.431372549019606,0.34658,10.182021194034153,0.27287014325459796,2.7415773839237785,0.17676389939136214,1.0,1.0,0.0,0.41352430110915517,0.6662116649574654,57.25701094840839,13.90517184260326,43.0,0,0,0,51,685.3,26.5,31.2,0.08110516934046345,0.02430200505969366
45
+ KNN (tuned),129.6893764515588,1.4108907164571591,9.839359763162042,0.11144693589219826,1.0,0.9979197340731149,0.11764705882352941,0.4687472868734289,0.6540729042725081,72.94693370250576,10.182721712640424,42.431372549019606,0.35012,10.182021194034153,0.08784447775946723,2.7415773839237785,0.03624739765555662,1.0,1.0,0.0,0.4456786012073394,0.740394718900499,57.25701094840839,2.2791618782574123,44.0,0,0,0,51,607.8,31.1,47.9,0.05837789661319073,0.023658093377554222
46
+ KNN (default),1.8202055170645122,0.18718713898544476,0.4967972483415847,0.03640307146637206,1.0,0.9999597757876026,0.11764705882352941,0.5414051085605402,0.9163581411737007,1.035103875827835,2.1647566343855695,43.833333333333336,0.40859,0.22567404641045463,0.036888705359564886,0.050692503527237594,0.021942545900661376,1.0,1.0,0.0,0.5109534123207524,1.0,1.0,1.2564234767690339,45.0,0,0,0,51,459.1,41.7,44.9,0.026515151515151516,0.022918450740172708
data/imputation_no/splits_all/tasks_all/datasets_all/leaderboard_overview_explorer.html DELETED
@@ -1,1411 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard explorer — no imputation | All Tasks | All Datasets</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Chart: a fixed y-axis pane beside a horizontally scrolling plot pane, so
269
- the axis stays readable however far right the reader scrolls. */
270
- .lb-chartwrap { position: relative; display: flex; align-items: flex-start; }
271
- .lb-axis { flex: none; }
272
- .lb-scroll {
273
- flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lb-scroll::-webkit-scrollbar { height: 10px; }
277
- .lb-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- .rangepick input[type=range] { width: 96px; accent-color: var(--accent); cursor: pointer; }
282
- .rangeval { font-variant-numeric: tabular-nums; font-weight: 500; min-width: 3.4em; }
283
- .lb-empty { padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
284
- /* Puts the family key on its own line of the legend strip. */
285
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
286
- /* Family blocks pack side by side rather than stacking: below a full-width
287
- chart, one row per family leaves the small ones (Other, Baseline) each
288
- wasting a line. The wide families still claim a row of their own. */
289
- .chips { margin-top: 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
290
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
291
- .chiprow { flex: 0 1 auto; min-width: 0; }
292
- </style>
293
- </head>
294
- <body>
295
- <div class="viewbar">
296
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
297
- </div>
298
- <p class="explorer-title" id="title"></p>
299
- <!-- One control row above everything it scopes (axis, sorting, variants, selection). -->
300
- <div class="controls">
301
- <label class="metricpick">Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <label class="metricpick">Sort
305
- <select id="sort-select"></select>
306
- </label>
307
- <label class="metricpick rangepick" title="Raise the axis floor to spread out the top of the field">Zoom
308
- <input type="range" id="ymin-range" aria-label="Y-axis minimum">
309
- <span class="rangeval" id="ymin-val"></span>
310
- </label>
311
- <div class="btnrow" id="variant-btns"></div>
312
- <div class="btnrow">
313
- <button class="btn" id="btn-all">All methods</button>
314
- <button class="btn" id="btn-top">Top 15</button>
315
- <button class="btn" id="btn-none">Clear</button>
316
- </div>
317
- <span class="hint">Click a column or chip to remove a method &middot; hover for exact values</span>
318
- </div>
319
- <div class="exportbar" id="exportbar" hidden>
320
- <span class="hint">Export figure</span>
321
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
322
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
323
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
324
- </div>
325
- <div class="legendstrip" id="legendstrip"></div>
326
- <div class="lb-chartwrap" id="chartwrap">
327
- <svg id="axis" class="lb-axis" role="presentation"></svg>
328
- <div class="lb-scroll" id="scroller">
329
- <svg id="chart" role="img" aria-label="Leaderboard overview"></svg>
330
- </div>
331
- <div class="tooltip"></div>
332
- </div>
333
- <div class="chips" id="chips"></div>
334
- <details class="datatable">
335
- <summary>Data table</summary>
336
- <div class="tblwrap" id="tblwrap"></div>
337
- </details>
338
-
339
- <script>
340
- (function () {
341
- "use strict";
342
- const CONFIG = {"title": null, "metrics": [{"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": "", "ci": {"lo": "elo_lo", "hi": "elo_hi"}}, {"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "score", "label": "Score", "axisLabel": "Score \u2014 higher is better", "lowerBetter": false, "fromZero": true, "decimals": 3, "suffix": ""}, {"key": "rank", "label": "Average rank", "axisLabel": "Average rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}, {"key": "hrank", "label": "Harmonic rank", "axisLabel": "Harmonic rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}], "rankMetric": "elo"};
343
- const POINTS = [{"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","reference":false,"elo":1792.0,"imp":4.074,"score":0.889,"rank":6.17,"hrank":1.72,"elo_hi":1907.0,"elo_lo":1682.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1691.0,"imp":7.979,"score":0.662,"rank":9.39,"hrank":4.03,"elo_hi":1773.0,"elo_lo":1623.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","reference":false,"elo":1665.0,"imp":9.328,"score":0.67,"rank":10.41,"hrank":4.06,"elo_hi":1745.0,"elo_lo":1604.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1620.0,"imp":10.751,"score":0.583,"rank":12.42,"hrank":7.14,"elo_hi":1691.0,"elo_lo":1569.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1595.0,"imp":10.427,"score":0.549,"rank":13.64,"hrank":6.22,"elo_hi":1669.0,"elo_lo":1539.0,"imputed_pct":0.0,"imputed":false},{"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","reference":false,"elo":1594.0,"imp":10.175,"score":0.606,"rank":13.71,"hrank":4.97,"elo_hi":1667.0,"elo_lo":1533.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1554.0,"imp":11.116,"score":0.504,"rank":15.81,"hrank":7.33,"elo_hi":1619.0,"elo_lo":1499.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1524.0,"imp":11.66,"score":0.474,"rank":17.55,"hrank":9.62,"elo_hi":1586.0,"elo_lo":1480.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1514.0,"imp":12.789,"score":0.401,"rank":18.2,"hrank":8.44,"elo_hi":1567.0,"elo_lo":1468.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1508.0,"imp":12.881,"score":0.405,"rank":18.55,"hrank":11.52,"elo_hi":1562.0,"elo_lo":1462.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","reference":false,"elo":1459.0,"imp":14.0,"score":0.38,"rank":21.7,"hrank":9.59,"elo_hi":1514.0,"elo_lo":1408.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1458.0,"imp":13.788,"score":0.375,"rank":21.81,"hrank":8.16,"elo_hi":1518.0,"elo_lo":1407.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1450.0,"imp":14.081,"score":0.315,"rank":22.33,"hrank":12.48,"elo_hi":1502.0,"elo_lo":1407.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1433.0,"imp":15.033,"score":0.262,"rank":23.53,"hrank":17.2,"elo_hi":1466.0,"elo_lo":1402.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1426.0,"imp":14.202,"score":0.295,"rank":23.99,"hrank":13.67,"elo_hi":1474.0,"elo_lo":1381.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1422.0,"imp":14.656,"score":0.261,"rank":24.34,"hrank":14.77,"elo_hi":1461.0,"elo_lo":1384.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1408.0,"imp":14.885,"score":0.245,"rank":25.3,"hrank":15.31,"elo_hi":1446.0,"elo_lo":1365.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1404.0,"imp":15.304,"score":0.235,"rank":25.56,"hrank":17.78,"elo_hi":1444.0,"elo_lo":1362.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1403.0,"imp":14.936,"score":0.316,"rank":25.69,"hrank":11.3,"elo_hi":1462.0,"elo_lo":1347.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1394.0,"imp":14.952,"score":0.251,"rank":26.35,"hrank":14.47,"elo_hi":1442.0,"elo_lo":1352.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1389.0,"imp":15.599,"score":0.277,"rank":26.66,"hrank":12.79,"elo_hi":1462.0,"elo_lo":1337.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1386.0,"imp":15.688,"score":0.194,"rank":26.93,"hrank":20.71,"elo_hi":1415.0,"elo_lo":1357.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1378.0,"imp":15.524,"score":0.224,"rank":27.47,"hrank":16.11,"elo_hi":1418.0,"elo_lo":1336.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1375.0,"imp":15.754,"score":0.198,"rank":27.73,"hrank":19.98,"elo_hi":1406.0,"elo_lo":1342.0,"imputed_pct":0.0,"imputed":false},{"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","reference":false,"elo":1363.0,"imp":15.526,"score":0.299,"rank":28.66,"hrank":9.55,"elo_hi":1441.0,"elo_lo":1304.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1355.0,"imp":16.044,"score":0.189,"rank":29.21,"hrank":16.82,"elo_hi":1392.0,"elo_lo":1318.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","reference":false,"elo":1350.0,"imp":16.081,"score":0.251,"rank":29.58,"hrank":17.68,"elo_hi":1408.0,"elo_lo":1298.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1350.0,"imp":16.065,"score":0.16,"rank":29.6,"hrank":22.22,"elo_hi":1379.0,"elo_lo":1318.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1348.0,"imp":16.284,"score":0.209,"rank":29.79,"hrank":18.96,"elo_hi":1397.0,"elo_lo":1305.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1333.0,"imp":17.213,"score":0.186,"rank":30.87,"hrank":19.34,"elo_hi":1379.0,"elo_lo":1273.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1325.0,"imp":17.225,"score":0.245,"rank":31.53,"hrank":14.33,"elo_hi":1389.0,"elo_lo":1270.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1308.0,"imp":17.551,"score":0.156,"rank":32.79,"hrank":21.32,"elo_hi":1352.0,"elo_lo":1248.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1300.0,"imp":17.449,"score":0.158,"rank":33.43,"hrank":22.28,"elo_hi":1345.0,"elo_lo":1261.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1298.0,"imp":17.0,"score":0.161,"rank":33.63,"hrank":21.84,"elo_hi":1345.0,"elo_lo":1254.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1295.0,"imp":17.147,"score":0.111,"rank":33.83,"hrank":23.47,"elo_hi":1335.0,"elo_lo":1262.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1285.0,"imp":17.161,"score":0.126,"rank":34.58,"hrank":25.37,"elo_hi":1333.0,"elo_lo":1238.0,"imputed_pct":0.0,"imputed":false},{"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","reference":false,"elo":1283.0,"imp":18.278,"score":0.194,"rank":34.72,"hrank":14.11,"elo_hi":1340.0,"elo_lo":1228.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1272.0,"imp":18.672,"score":0.116,"rank":35.64,"hrank":25.8,"elo_hi":1312.0,"elo_lo":1232.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1264.0,"imp":18.657,"score":0.121,"rank":36.23,"hrank":23.23,"elo_hi":1311.0,"elo_lo":1209.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1248.0,"imp":19.194,"score":0.09,"rank":37.44,"hrank":23.68,"elo_hi":1289.0,"elo_lo":1209.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1235.0,"imp":19.314,"score":0.082,"rank":38.43,"hrank":28.17,"elo_hi":1279.0,"elo_lo":1195.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1230.0,"imp":18.569,"score":0.066,"rank":38.85,"hrank":29.23,"elo_hi":1266.0,"elo_lo":1193.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1214.0,"imp":18.792,"score":0.053,"rank":40.04,"hrank":30.76,"elo_hi":1253.0,"elo_lo":1178.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1212.0,"imp":18.877,"score":0.069,"rank":40.24,"hrank":30.96,"elo_hi":1253.0,"elo_lo":1170.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1208.0,"imp":19.777,"score":0.083,"rank":40.47,"hrank":28.16,"elo_hi":1257.0,"elo_lo":1165.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1207.0,"imp":19.677,"score":0.105,"rank":40.58,"hrank":25.38,"elo_hi":1258.0,"elo_lo":1148.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1204.0,"imp":20.255,"score":0.069,"rank":40.82,"hrank":28.54,"elo_hi":1249.0,"elo_lo":1155.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1186.0,"imp":19.344,"score":0.038,"rank":42.12,"hrank":36.15,"elo_hi":1220.0,"elo_lo":1157.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1180.0,"imp":20.564,"score":0.074,"rank":42.56,"hrank":26.73,"elo_hi":1225.0,"elo_lo":1128.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1171.0,"imp":20.828,"score":0.06,"rank":43.28,"hrank":29.04,"elo_hi":1225.0,"elo_lo":1124.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1147.0,"imp":21.096,"score":0.048,"rank":45.01,"hrank":31.19,"elo_hi":1199.0,"elo_lo":1088.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1137.0,"imp":21.535,"score":0.041,"rank":45.71,"hrank":28.56,"elo_hi":1187.0,"elo_lo":1093.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":1097.0,"imp":25.584,"score":0.125,"rank":48.47,"hrank":15.95,"elo_hi":1172.0,"elo_lo":1014.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":1096.0,"imp":25.6,"score":0.123,"rank":48.56,"hrank":15.35,"elo_hi":1175.0,"elo_lo":1013.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1094.0,"imp":23.024,"score":0.037,"rank":48.66,"hrank":33.93,"elo_hi":1141.0,"elo_lo":1040.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1089.0,"imp":26.446,"score":0.024,"rank":49.04,"hrank":38.83,"elo_hi":1133.0,"elo_lo":1044.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1074.0,"imp":22.815,"score":0.011,"rank":49.99,"hrank":39.42,"elo_hi":1108.0,"elo_lo":1032.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1050.0,"imp":27.802,"score":0.011,"rank":51.52,"hrank":39.96,"elo_hi":1097.0,"elo_lo":1000.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1043.0,"imp":26.445,"score":0.029,"rank":51.95,"hrank":44.11,"elo_hi":1104.0,"elo_lo":973.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1009.0,"imp":26.176,"score":0.012,"rank":54.0,"hrank":47.38,"elo_hi":1060.0,"elo_lo":943.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1008.0,"imp":25.682,"score":0.009,"rank":54.07,"hrank":39.97,"elo_hi":1065.0,"elo_lo":946.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1000.0,"imp":26.265,"score":0.003,"rank":54.51,"hrank":49.81,"elo_hi":1047.0,"elo_lo":953.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":997.0,"imp":27.822,"score":0.017,"rank":54.67,"hrank":38.92,"elo_hi":1058.0,"elo_lo":921.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":993.0,"imp":30.456,"score":0.077,"rank":54.89,"hrank":24.31,"elo_hi":1082.0,"elo_lo":889.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":965.0,"imp":33.667,"score":0.031,"rank":56.39,"hrank":40.87,"elo_hi":1029.0,"elo_lo":865.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":940.0,"imp":34.211,"score":0.022,"rank":57.67,"hrank":42.64,"elo_hi":1010.0,"elo_lo":835.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":936.0,"imp":31.748,"score":0.0,"rank":57.84,"hrank":45.44,"elo_hi":979.0,"elo_lo":879.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":890.0,"imp":32.782,"score":0.008,"rank":59.92,"hrank":46.93,"elo_hi":951.0,"elo_lo":790.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":861.0,"imp":36.822,"score":0.014,"rank":61.12,"hrank":47.51,"elo_hi":930.0,"elo_lo":743.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":654.0,"imp":45.987,"score":0.0,"rank":66.89,"hrank":60.42,"elo_hi":732.0,"elo_lo":563.0,"imputed_pct":0.0,"imputed":false}];
344
-
345
-
346
- const NS = "http://www.w3.org/2000/svg";
347
- // Baseline and Other are one bucket, as in the site's own type legend: they
348
- // already share a color, and each holds only a handful of methods.
349
- const FAM_MERGED = "Baseline / Other";
350
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
351
- // Normalized up front so every later lookup — colors, chips, sorting — sees
352
- // the merged family. Both templates declare POINTS above this block.
353
- for (const p of POINTS) p.family = famOf(p.family);
354
-
355
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
356
- const FAM_VAR = {
357
- "Foundation Model": "var(--fam-foundation)",
358
- "Tree-based": "var(--fam-tree)",
359
- "Neural Network": "var(--fam-nn)",
360
- "Reference Pipeline": "var(--fam-reference)",
361
- [FAM_MERGED]: "var(--fam-baseline)",
362
- };
363
- // The symbol the website shows for each family, so a family chip here reads the
364
- // same as the Type column on the site. Baseline and Other are one bucket, so
365
- // that chip carries both symbols.
366
- const FAM_SYMBOL = {
367
- "Foundation Model": "🧠⚡",
368
- "Tree-based": "🌳",
369
- "Neural Network": "🧠🔁",
370
- "Reference Pipeline": "📊",
371
- [FAM_MERGED]: "📏 ❓",
372
- };
373
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
374
- const VARIANT_VAR = {
375
- "Default": "var(--var-default)",
376
- "Tuned": "var(--var-tuned)",
377
- "Tuned + Ens.": "var(--var-tunedens)",
378
- };
379
-
380
- // A family chip's label: its symbol, its name and how many methods it holds.
381
- function famChipLabel(family, count) {
382
- const symbol = FAM_SYMBOL[family];
383
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
384
- family + ' <span class="count">&times;' + count + "</span>";
385
- }
386
-
387
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
388
- const FAM_INK = {
389
- "Foundation Model": "var(--fam-foundation-ink)",
390
- "Tree-based": "var(--fam-tree-ink)",
391
- "Neural Network": "var(--fam-nn-ink)",
392
- "Reference Pipeline": "var(--fam-reference-ink)",
393
- [FAM_MERGED]: "var(--fam-baseline-ink)",
394
- };
395
-
396
- // Create an SVG element with attributes, optionally appended to `parent`.
397
- function el(name, attrs, parent) {
398
- const node = document.createElementNS(NS, name);
399
- for (const k in attrs) node.setAttribute(k, attrs[k]);
400
- if (parent) parent.appendChild(node);
401
- return node;
402
- }
403
-
404
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
405
- // locale-independent by definition, which is the point: `toLocaleString`
406
- // would follow the *viewer's* browser locale and print 1234,5 for a German
407
- // visitor, disagreeing with the figures and CSVs beside it.
408
- function fmtNum(v, decimals) {
409
- if (v == null || !isFinite(v)) return "—";
410
- return v.toFixed(decimals);
411
- }
412
-
413
- function fmtMetric(metric, v) {
414
- if (v == null || !isFinite(v)) return "—";
415
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
416
- }
417
-
418
- function fmtTime(v) {
419
- if (v >= 100) return fmtNum(v, 0) + " s";
420
- if (v >= 1) return fmtNum(v, 1) + " s";
421
- if (v >= 0.1) return fmtNum(v, 2) + " s";
422
- return fmtNum(v, 3) + " s";
423
- }
424
-
425
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
426
- function niceStep(raw) {
427
- if (!(raw > 0)) return 1;
428
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
429
- for (const m of [1, 2, 2.5, 5, 10]) {
430
- if (mag * m >= raw) return mag * m;
431
- }
432
- return mag * 10;
433
- }
434
-
435
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
436
- // index rather than by accumulation so fractional steps do not drift.
437
- function ticks(min, max, target) {
438
- const step = niceStep((max - min) / target);
439
- const first = Math.ceil(min / step);
440
- const out = [];
441
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
442
- return out.length ? out : [min];
443
- }
444
-
445
- // A hover tooltip anchored inside `box` (which must be position:relative).
446
- function makeTooltip(box) {
447
- const node = box.querySelector(".tooltip");
448
- return {
449
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
450
- move(ev) {
451
- const r = box.getBoundingClientRect();
452
- let tx = ev.clientX - r.left + 14;
453
- const ty = ev.clientY - r.top + 12;
454
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
455
- node.style.left = tx + "px";
456
- node.style.top = ty + "px";
457
- },
458
- hide() { node.style.display = "none"; },
459
- };
460
- }
461
-
462
- // Paper view — white surface, legend + chart only — is the *default*: what a
463
- // reader wants first is the figure, and it is the state worth exporting. The
464
- // controls, chip list and data table are one click away behind "Edit view".
465
- // `afterToggle` re-renders charts whose size is measured from the layout.
466
- // `options.openInPaper` (default true) decides the state the page opens in. A
467
- // chart opens as the figure; the leaderboard table opens with its controls,
468
- // since there the interaction is the point rather than scaffolding around it.
469
- function setUpPaperView(afterToggle, options) {
470
- const opts = options || {};
471
- const root = document.documentElement;
472
- let hostTheme = null; // the embedding page's choice, captured on entry
473
- const btn = document.getElementById("btn-paper");
474
- const embedded = window.parent !== window;
475
-
476
- function setPaper(on) {
477
- document.body.classList.toggle("paper", on);
478
- if (on) {
479
- hostTheme = root.getAttribute("data-theme");
480
- root.setAttribute("data-theme", "light");
481
- } else if (hostTheme) {
482
- root.setAttribute("data-theme", hostTheme);
483
- } else {
484
- root.removeAttribute("data-theme");
485
- }
486
- btn.textContent = on ? "Edit view" : "Paper view";
487
- document.getElementById("exportbar").hidden = !on || embedded;
488
- if (afterToggle) requestAnimationFrame(afterToggle);
489
- postHeight();
490
- }
491
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
492
- // Embedded, the host page owns these controls — they sit beside the panel's
493
- // static-figure toggle and are driven from the outside. Standalone (the
494
- // shareable single file) this page needs its own.
495
- if (embedded) document.querySelector(".viewbar").hidden = true;
496
- window.addEventListener("message", ev => {
497
- const d = ev.data;
498
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
499
- });
500
- // Only standalone: embedded, the host owns the button and would not see the
501
- // key press, so its label would fall out of step with the frame.
502
- if (!embedded) {
503
- document.addEventListener("keydown", ev => {
504
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
505
- });
506
- }
507
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
508
- }
509
-
510
- // --- Figure export ---------------------------------------------------------
511
- // The chart is live SVG, so a file can be built from it directly. Three things
512
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
513
- // nothing outside this document), it has no background or font of its own, and
514
- // the legend is HTML rather than part of the SVG.
515
-
516
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
517
- // cloned and its label re-emitted at the measured position. foreignObject would
518
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
519
- // break the PNG path.
520
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
521
- // once the node leaves this document.
522
- function resolveVars(root, resolve) {
523
- for (const node of [root, ...root.querySelectorAll("*")]) {
524
- for (const attr of ["fill", "stroke"]) {
525
- const value = node.getAttribute(attr);
526
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
527
- }
528
- }
529
- }
530
-
531
- function legendToSvg(container, resolve) {
532
- const base = container.getBoundingClientRect();
533
- const group = document.createElementNS(NS, "g");
534
- let height = 0;
535
- for (const item of container.querySelectorAll(".item")) {
536
- const box = item.getBoundingClientRect();
537
- if (!box.width) continue;
538
- height = Math.max(height, box.bottom - base.top);
539
- let textLeft = box.left - base.left;
540
- const glyph = item.querySelector("svg");
541
- if (glyph) {
542
- const gbox = glyph.getBoundingClientRect();
543
- const wrap = el("g", {
544
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
545
- }, group);
546
- const glyphClone = glyph.cloneNode(true);
547
- resolveVars(glyphClone, resolve);
548
- wrap.appendChild(glyphClone);
549
- textLeft = gbox.right - base.left + 5;
550
- }
551
- const label = item.textContent.trim();
552
- if (!label) continue;
553
- const colored = item.querySelector("[style*='color']");
554
- const text = el("text", {
555
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
556
- fill: resolve(getComputedStyle(colored || item).color),
557
- }, group);
558
- text.textContent = label;
559
- }
560
- return { group, height: Math.ceil(height) };
561
- }
562
-
563
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
564
- // y-axis beside the scrolling plot) still exports as one figure.
565
- function buildExportSvg(parts, legendEl, pad = 10) {
566
- const rootStyle = getComputedStyle(document.documentElement);
567
- const resolve = value => String(value).replace(
568
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
569
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
570
-
571
- let chartW = 0, chartH = 0;
572
- for (const part of parts) {
573
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
574
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
575
- }
576
-
577
- const out = document.createElementNS(NS, "svg");
578
- out.setAttribute("xmlns", NS);
579
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
580
- let top = pad;
581
- const later = []; // built after the width is known
582
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
583
- if (legend && legend.height) {
584
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
585
- later.push(() => out.appendChild(legend.group));
586
- top += legend.height + 8;
587
- }
588
-
589
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
590
- const height = top + chartH + pad;
591
- out.setAttribute("width", Math.ceil(width));
592
- out.setAttribute("height", Math.ceil(height));
593
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
594
- for (const build of later) build();
595
-
596
- for (const part of parts) {
597
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
598
- const clone = part.svg.cloneNode(true);
599
- resolveVars(clone, resolve);
600
- while (clone.firstChild) group.appendChild(clone.firstChild);
601
- }
602
- return out;
603
- }
604
-
605
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
606
- function slugify(text) {
607
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
608
- }
609
-
610
- function downloadUrl(url, filename) {
611
- const a = document.createElement("a");
612
- a.href = url;
613
- a.download = filename;
614
- document.body.appendChild(a);
615
- a.click();
616
- a.remove();
617
- }
618
-
619
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
620
- function rasterize(svg, scale, done, fail) {
621
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
622
- const img = new Image();
623
- img.onload = () => {
624
- const canvas = document.createElement("canvas");
625
- canvas.width = Math.round(width * scale);
626
- canvas.height = Math.round(height * scale);
627
- const ctx = canvas.getContext("2d");
628
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
629
- ctx.drawImage(img, 0, 0);
630
- done(canvas, width, height);
631
- };
632
- img.onerror = fail;
633
- img.src = "data:image/svg+xml;charset=utf-8,"
634
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
635
- }
636
-
637
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
638
- // cost this page its zero-dependency, single-file property. The image is stored
639
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
640
- // in points to the figure's CSS size, so it prints at the size it appears here
641
- // and the pixels land at 96*scale dpi.
642
- async function buildPdf(canvas, cssWidth, cssHeight) {
643
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
644
- const rgb = new Uint8Array((pixels.length / 4) * 3);
645
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
646
- rgb[j] = pixels[i];
647
- rgb[j + 1] = pixels[i + 1];
648
- rgb[j + 2] = pixels[i + 2];
649
- }
650
- const deflated = new Uint8Array(await new Response(
651
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
652
-
653
- const encoder = new TextEncoder();
654
- const chunks = [];
655
- const offsets = [];
656
- let cursor = 0;
657
- const put = data => {
658
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
659
- chunks.push(bytes);
660
- cursor += bytes.length;
661
- };
662
- const object = (id, body, stream) => {
663
- offsets[id] = cursor;
664
- put(`${id} 0 obj\n${body}\n`);
665
- if (stream) {
666
- put("stream\n");
667
- put(stream);
668
- put("\nendstream\n");
669
- }
670
- put("endobj\n");
671
- };
672
-
673
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
674
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
675
- put("%PDF-1.4\n");
676
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
677
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
678
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
679
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
680
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
681
- object(4, "<< /Type /XObject /Subtype /Image "
682
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
683
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
684
- object(5, `<< /Length ${content.length} >>`, content);
685
-
686
- const xref = cursor;
687
- let table = "xref\n0 6\n0000000000 65535 f \n";
688
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
689
- put(table);
690
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
691
- return new Blob(chunks, { type: "application/pdf" });
692
- }
693
-
694
- // Wire up the export buttons; `getParts` is called per click so the file always
695
- // matches what is on screen. Returns a `run(format)` the host page can drive.
696
- function setUpExport(getParts, basename) {
697
- const buttons = {
698
- svg: document.getElementById("btn-svg"),
699
- png: document.getElementById("btn-png"),
700
- pdf: document.getElementById("btn-pdf"),
701
- };
702
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
703
-
704
- // A sandboxed frame has no modals, so a failure is reported on the button.
705
- function complain(format) {
706
- const button = buttons[format];
707
- if (!button) return;
708
- const label = button.textContent;
709
- button.textContent = "failed";
710
- setTimeout(() => { button.textContent = label; }, 2500);
711
- }
712
-
713
- function run(format) {
714
- const svg = figure();
715
- const name = basename();
716
- if (format === "svg") {
717
- downloadUrl("data:image/svg+xml;charset=utf-8,"
718
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
719
- return;
720
- }
721
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
722
- // points so the pixels already land near 200 dpi at print size.
723
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
724
- if (format === "png") {
725
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
726
- } else {
727
- buildPdf(canvas, cssWidth, cssHeight)
728
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
729
- .catch(() => complain("pdf"));
730
- }
731
- }, () => complain(format));
732
- }
733
-
734
- for (const format of Object.keys(buttons)) {
735
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
736
- }
737
- // Embedded, the buttons live in the host's panel header (see main.taExport).
738
- window.addEventListener("message", ev => {
739
- const d = ev.data;
740
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
741
- });
742
- }
743
-
744
- // When embedded, report the content height so the host page can size the
745
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
746
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
747
- // clamped to at least the iframe's current viewport, which turns the
748
- // resize round-trip into a grow-forever feedback loop. The change guard
749
- // stops re-posting once the height settles.
750
- let lastPostedHeight = 0;
751
- function postHeight() {
752
- if (window.parent === window) return;
753
- const height = Math.ceil(document.body.offsetHeight);
754
- if (Math.abs(height - lastPostedHeight) < 3) return;
755
- lastPostedHeight = height;
756
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
757
- }
758
-
759
-
760
- // ---------- geometry ----------
761
- const AXIS_W = 72; // width of the sticky y-axis pane
762
- const PLOT_H = 320; // height of the plot area itself
763
- const TOP = 14; // headroom above the tallest bar
764
- const LABEL_TOP = 18; // axis line -> first label row
765
- const LABEL_ROW = 19; // vertical offset of the staggered second row
766
- const LABEL_SIZE = 14; // method names; the slot below scales with it
767
- const TICK_SIZE = 12.5;
768
- // Method names are set horizontally (rotated ones are markedly harder to
769
- // read), staggered over two rows exactly like the static figure — so a slot
770
- // has to be wide enough for half a name. Past ~24 methods the chart scrolls
771
- // rather than squeezing every column into the viewport.
772
- // No upper bound on the slot: with few methods selected a capped slot left
773
- // the columns huddled on the left with dead space beside them, so the slots
774
- // simply share out whatever width there is.
775
- const MIN_SLOT = 66;
776
- const BAR_FRAC = 0.88; // share of the slot the widest bar takes
777
- const MAX_BAR = 70; // ...but never wider than this, however few columns
778
-
779
- // Variant -> (color token, width relative to the widest bar). The bars are
780
- // concentric, so the nesting itself encodes the tuning progression.
781
- const VARIANT_STYLE = {
782
- "Tuned + Ens.": { color: "var(--var-tunedens)", rel: 1 },
783
- "Tuned": { color: "var(--var-tuned)", rel: 0.8 },
784
- "Default": { color: "var(--var-default)", rel: 0.6 },
785
- };
786
- const VARIANT_ORDER = ["Default", "Tuned", "Tuned + Ens."];
787
- // Dash patterns cycle so several reference lines stay distinguishable.
788
- const REF_DASHES = ["8 5", "2 4", "12 4 3 4"];
789
-
790
- const titleEl = document.getElementById("title");
791
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
792
-
793
- const axisSvg = document.getElementById("axis");
794
- const svg = document.getElementById("chart");
795
- const scroller = document.getElementById("scroller");
796
- const wrap = document.getElementById("chartwrap");
797
- const yminRange = document.getElementById("ymin-range");
798
- const yminVal = document.getElementById("ymin-val");
799
- const tip = makeTooltip(wrap);
800
-
801
- // ---------- data ----------
802
- const METRICS = CONFIG.metrics;
803
- const metricByKey = {};
804
- for (const m of METRICS) metricByKey[m.key] = m;
805
-
806
- // One entry per method (its variants grouped); reference pipelines are kept
807
- // apart — they are drawn as threshold lines, not as columns.
808
- const byMethod = new Map();
809
- const refs = [];
810
- for (const p of POINTS) {
811
- if (p.reference) { refs.push(p); continue; }
812
- let entry = byMethod.get(p.method);
813
- if (!entry) {
814
- entry = { method: p.method, family: p.family, url: p.url, points: [] };
815
- byMethod.set(p.method, entry);
816
- }
817
- entry.points.push(p);
818
- }
819
- for (const e of byMethod.values()) {
820
- e.points.sort((a, b) => VARIANT_ORDER.indexOf(a.variant) - VARIANT_ORDER.indexOf(b.variant));
821
- e.imputed = e.points.some(p => p.imputed);
822
- e.imputed_pct = Math.max(...e.points.map(p => p.imputed_pct || 0));
823
- }
824
-
825
- const state = {
826
- metric: METRICS[0].key,
827
- sort: "best",
828
- yMin: null, // null = the automatic axis floor; a number = zoomed in
829
-
830
- methods: new Set(byMethod.keys()),
831
- refs: new Set(refs.map(r => r.method)),
832
- variants: new Set(VARIANT_ORDER),
833
- };
834
-
835
- function metric() { return metricByKey[state.metric]; }
836
-
837
- // A method's best value under `m`, ignoring variants the reader switched off.
838
- function bestOf(entry, m) {
839
- const vals = entry.points
840
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
841
- .map(p => p[m.key]);
842
- if (!vals.length) return null;
843
- return m.lowerBetter ? Math.min(...vals) : Math.max(...vals);
844
- }
845
-
846
- function sortedMethods(entries, m) {
847
- const arr = [...entries];
848
- const cmp = {
849
- best: (a, b) => rankVal(a, m) - rankVal(b, m),
850
- worst: (a, b) => rankVal(b, m) - rankVal(a, m),
851
- name: (a, b) => a.method.localeCompare(b.method),
852
- family: (a, b) =>
853
- FAM_ORDER.indexOf(a.family) - FAM_ORDER.indexOf(b.family) || rankVal(a, m) - rankVal(b, m),
854
- }[state.sort];
855
- return arr.sort(cmp);
856
- }
857
- // Sort key that puts "better" first for either metric direction, with
858
- // value-less methods last.
859
- function rankVal(entry, m) {
860
- const v = bestOf(entry, m);
861
- if (v == null) return Infinity;
862
- return m.lowerBetter ? v : -v;
863
- }
864
-
865
- function visibleEntries() {
866
- const m = metric();
867
- return sortedMethods([...byMethod.values()].filter(e => state.methods.has(e.method) && bestOf(e, m) != null), m);
868
- }
869
- function visibleRefs() {
870
- return refs.filter(r => state.refs.has(r.method) && r[state.metric] != null);
871
- }
872
-
873
- // Rendered width of each label, measured in the live document (font metrics
874
- // are not knowable up front): it decides whether the names fit on one row or
875
- // need the two-row stagger, and which ones have to be shortened.
876
- function measureLabels(names) {
877
- const probe = el("g", { visibility: "hidden" }, svg);
878
- const widths = names.map(name => {
879
- const t = el("text", { "font-size": LABEL_SIZE }, probe);
880
- t.textContent = name;
881
- return t.getComputedTextLength();
882
- });
883
- probe.remove();
884
- return widths;
885
- }
886
-
887
- // Trim a name to `budget` px, ending in an ellipsis. The full name stays one
888
- // hover (and one data-table row) away.
889
- function fitLabel(node, name, width, budget) {
890
- if (width <= budget) return;
891
- let text = name;
892
- while (text.length > 1 && node.getComputedTextLength() > budget) {
893
- text = text.slice(0, -1);
894
- node.textContent = text + "…";
895
- }
896
- }
897
-
898
- // ---------- chart ----------
899
- function render() {
900
- const m = metric();
901
- const entries = visibleEntries();
902
- const shownRefs = visibleRefs();
903
- svg.textContent = "";
904
- axisSvg.textContent = "";
905
- axisSvg.setAttribute("width", AXIS_W);
906
-
907
- const avail = Math.max(240, scroller.clientWidth - 2);
908
- if (!entries.length) {
909
- axisSvg.setAttribute("height", 120);
910
- svg.setAttribute("width", avail);
911
- svg.setAttribute("height", 120);
912
- const t = el("text", { x: avail / 2, y: 60, "text-anchor": "middle", "font-size": 13, fill: "var(--muted)" }, svg);
913
- t.textContent = "No methods selected — use “All methods” to bring them back.";
914
- buildLegend(m, shownRefs);
915
- postHeight();
916
- return;
917
- }
918
-
919
- const slot = Math.max(MIN_SLOT, avail / entries.length);
920
- const barUnit = Math.min(MAX_BAR, slot * BAR_FRAC);
921
- const plotW = Math.max(avail, slot * entries.length);
922
- // Names go on one row when they fit side by side, otherwise on two
923
- // staggered rows (each label then has two slots of room).
924
- const labels = entries.map(e => e.method + (e.imputed ? " ‡" : ""));
925
- const labelWidths = measureLabels(labels);
926
- const widest = Math.max(...labelWidths);
927
- const labelRows = widest <= slot - 6 ? 1 : 2;
928
- const H = TOP + PLOT_H + LABEL_TOP + (labelRows - 1) * LABEL_ROW + 10;
929
- svg.setAttribute("width", plotW);
930
- svg.setAttribute("height", H);
931
- axisSvg.setAttribute("height", H);
932
-
933
- // -- y domain: from zero where the metric has one, else a floor below the
934
- // shortest bar (Elo has no meaningful zero, and starting at zero would
935
- // squash every difference into the top fifth of the chart). The floor
936
- // ignores the CI whiskers so one wide interval cannot deflate the scale.
937
- const barVals = [];
938
- const allVals = [];
939
- for (const e of entries) {
940
- for (const p of e.points) {
941
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
942
- barVals.push(p[m.key]);
943
- allVals.push(p[m.key]);
944
- if (m.ci && p[m.ci.hi] != null) allVals.push(p[m.ci.hi]);
945
- }
946
- }
947
- for (const r of shownRefs) { barVals.push(r[state.metric]); allVals.push(r[state.metric]); }
948
- const barMin = Math.min(...barVals), barMax = Math.max(...allVals);
949
- const span = barMax - barMin || Math.abs(barMax) || 1;
950
- let autoY0, y1;
951
- if (m.fromZero) {
952
- autoY0 = 0;
953
- y1 = barMax * 1.06 || 1;
954
- } else {
955
- // Snap the floor down to a tick multiple so the axis reads in round
956
- // numbers, and keep it just below the shortest bar: every bar stays
957
- // visible (the static figure clips the ones below its fixed floor).
958
- const step = niceStep(span / 6);
959
- autoY0 = Math.floor((barMin - span * 0.05) / step) * step;
960
- y1 = barMax + span * 0.04;
961
- }
962
- // The zoom slider raises the floor from there toward the top of the field,
963
- // magnifying the differences between the leaders. Its bounds follow the
964
- // metric's own scale, so they are refreshed on every render; the bars and
965
- // whiskers that drop below the new floor are clipped away (see plot-clip).
966
- const zoomMax = autoY0 + (y1 - autoY0) * 0.9;
967
- yminRange.min = autoY0;
968
- yminRange.max = zoomMax;
969
- yminRange.step = (zoomMax - autoY0) / 200 || 1;
970
- if (state.yMin != null) state.yMin = Math.min(state.yMin, zoomMax);
971
- if (state.yMin != null && state.yMin <= autoY0) state.yMin = null;
972
- const y0 = state.yMin == null ? autoY0 : state.yMin;
973
- yminRange.value = y0;
974
- yminVal.textContent = state.yMin == null ? "auto" : fmtNum(y0, m.decimals);
975
- const Y = v => TOP + (1 - (v - y0) / (y1 - y0)) * PLOT_H;
976
- const baseY = Y(y0);
977
-
978
- // -- grid (solid hairlines) in the plot pane, tick labels in the axis pane
979
- const tickVals = ticks(y0, y1, 6);
980
- const grid = el("g", {}, svg);
981
- const tickLabels = [];
982
- for (const tv of tickVals) {
983
- const gy = Y(tv);
984
- el("line", { x1: 0, y1: gy, x2: plotW, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
985
- const t = el("text", {
986
- x: AXIS_W - 10, y: gy + 4, "text-anchor": "end", "font-size": TICK_SIZE, fill: "var(--muted)",
987
- }, axisSvg);
988
- t.textContent = fmtNum(tv, m.decimals);
989
- tickLabels.push({ y: gy, node: t });
990
- }
991
- el("line", { x1: 0, y1: baseY, x2: plotW, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, grid);
992
- el("line", { x1: AXIS_W - 1, y1: TOP, x2: AXIS_W - 1, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, axisSvg);
993
- el("text", {
994
- x: 0, y: 0, "text-anchor": "middle", "font-size": LABEL_SIZE, "font-weight": 650,
995
- fill: "var(--ink)", transform: `translate(15 ${TOP + PLOT_H / 2}) rotate(-90)`,
996
- }, axisSvg).textContent = m.axisLabel;
997
-
998
- // -- hover band behind the marks (highlights the whole column group)
999
- const band = el("rect", {
1000
- x: 0, y: TOP, width: slot, height: PLOT_H + 6, fill: "var(--ink)", opacity: 0, "pointer-events": "none",
1001
- }, svg);
1002
-
1003
- const defs = el("defs", {}, svg);
1004
- // -- imputed hatch, reused by every partially imputed bar. Crossed diagonals,
1005
- // matching the "x" hatch matplotlib draws in the static figure; the
1006
- // diagonals meet the tile corners exactly, so the grid tiles seamlessly.
1007
- const pat = el("pattern", {
1008
- id: "imp-hatch", width: 8, height: 8, patternUnits: "userSpaceOnUse",
1009
- }, defs);
1010
- el("path", {
1011
- d: "M0,0 L8,8 M8,0 L0,8", stroke: "var(--paper)", "stroke-width": 1.5,
1012
- opacity: 0.85, fill: "none",
1013
- }, pat);
1014
-
1015
- // -- Everything data-bearing is clipped to the plot area. The axis floor is
1016
- // set from the bar values (a couple of very wide intervals would
1017
- // otherwise deflate the whole scale), so a long lower CI whisker can
1018
- // reach past it and run into the method names below. Clip it there; the
1019
- // exact interval stays in the tooltip and the data table.
1020
- const clip = el("clipPath", { id: "plot-clip" }, defs);
1021
- el("rect", { x: 0, y: 0, width: plotW, height: baseY }, clip);
1022
-
1023
- // -- bars: one concentric group per method
1024
- const barsG = el("g", { "clip-path": "url(#plot-clip)" }, svg);
1025
- entries.forEach((entry, i) => {
1026
- const cx = i * slot + slot / 2;
1027
- // Widest bar first, narrowest last: the bars are concentric, so painting
1028
- // them by width guarantees each one stays visible. Ordering by height
1029
- // instead loses a variant outright whenever a narrower bar is the taller
1030
- // of the two (TabSTAR's tuned bar sat 1 Elo above tuned + ensembled, and
1031
- // the wider bar covered it completely).
1032
- const relOf = p => (VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"]).rel;
1033
- const drawn = entry.points
1034
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
1035
- .slice()
1036
- .sort((a, b) => relOf(b) - relOf(a));
1037
- for (const p of drawn) {
1038
- const style = VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"];
1039
- const w = barUnit * style.rel;
1040
- const top = Y(p[m.key]);
1041
- const rect = {
1042
- x: cx - w / 2, y: Math.min(top, baseY), width: w, height: Math.max(1, Math.abs(baseY - top)),
1043
- };
1044
- // 2px surface ring, not a border: it is what keeps a nested bar legible
1045
- // against the wider bar it sits inside.
1046
- el("rect", { ...rect, fill: style.color, stroke: "var(--paper)", "stroke-width": 1.5, rx: 2 }, barsG);
1047
- if (p.imputed) el("rect", { ...rect, fill: "url(#imp-hatch)", rx: 2 }, barsG);
1048
- if (m.ci && p[m.ci.lo] != null) {
1049
- const cap = Math.max(2.5, w * 0.34);
1050
- const whisk = el("g", {
1051
- stroke: `color-mix(in srgb, ${style.color} 55%, var(--ink))`, "stroke-width": 1.4, opacity: 0.9,
1052
- }, barsG);
1053
- el("line", { x1: cx, y1: Y(p[m.ci.lo]), x2: cx, y2: Y(p[m.ci.hi]) }, whisk);
1054
- el("line", { x1: cx - cap, y1: Y(p[m.ci.hi]), x2: cx + cap, y2: Y(p[m.ci.hi]) }, whisk);
1055
- el("line", { x1: cx - cap, y1: Y(p[m.ci.lo]), x2: cx + cap, y2: Y(p[m.ci.lo]) }, whisk);
1056
- }
1057
- }
1058
- });
1059
-
1060
- // -- x axis: the method name, colored by model family (the legend below
1061
- // names the colors).
1062
- const xg = el("g", {}, svg);
1063
- // Hairlines share one group that is painted before every name, so a name
1064
- // nudged sideways at the edge (see below) passes over a neighbouring
1065
- // column's hairline instead of being crossed out by it.
1066
- const xLines = el("g", {}, xg);
1067
- const xLabels = el("g", {}, xg);
1068
- entries.forEach((entry, i) => {
1069
- const cx = i * slot + slot / 2;
1070
- const row = labelRows === 1 ? 0 : i % 2;
1071
- const y = baseY + LABEL_TOP + row * LABEL_ROW;
1072
- // A hairline drops the staggered row back to its own column.
1073
- if (row) {
1074
- el("line", {
1075
- x1: cx, y1: baseY + 3, x2: cx, y2: y - 10,
1076
- stroke: "var(--line)", "stroke-width": 1,
1077
- }, xLines);
1078
- }
1079
- const t = el("text", {
1080
- x: cx, y, "font-size": LABEL_SIZE, "text-anchor": "middle", "font-weight": 650,
1081
- fill: FAM_INK[entry.family] || "var(--ink)",
1082
- // A halo in the surface color keeps the glyphs legible where a nudged
1083
- // name crosses a hairline, rather than the line running through them.
1084
- "paint-order": "stroke", stroke: "var(--paper)", "stroke-width": 3,
1085
- }, xLabels);
1086
- t.textContent = labels[i];
1087
- fitLabel(t, labels[i], labelWidths[i], slot * labelRows - 8);
1088
- // The outermost columns sit only half a slot from the edge, so a name
1089
- // wider than one slot would reach past the SVG viewport and be cut off
1090
- // there (a name may occupy two slots when the rows are staggered). Nudge
1091
- // it inwards by just enough to stay whole; every other label keeps its
1092
- // column centre, and the bar below still marks the column.
1093
- const half = t.getComputedTextLength() / 2;
1094
- t.setAttribute("x", Math.max(half + 1, Math.min(cx, plotW - half - 1)));
1095
- });
1096
-
1097
- // -- reference pipelines as threshold lines. Their names live in the legend
1098
- // (matched by dash pattern) rather than on the line, where they would
1099
- // cover the tallest bars at every scroll position.
1100
- const refG = el("g", {}, svg);
1101
- const tagYs = [];
1102
- shownRefs.forEach((r, i) => {
1103
- const ry = Y(r[state.metric]);
1104
- if (ry < TOP || ry > baseY) return;
1105
- el("line", {
1106
- x1: 0, y1: ry, x2: plotW, y2: ry, stroke: "var(--fam-reference)", "stroke-width": 1.8,
1107
- "stroke-dasharray": REF_DASHES[i % REF_DASHES.length], opacity: 0.95,
1108
- }, refG);
1109
- // Sticky value tag in the axis pane, so the threshold stays readable at
1110
- // any scroll position. Two nearby thresholds would print on top of each
1111
- // other, so nudge each tag clear of the ones already placed; a tag wins
1112
- // over a tick label it would sit on.
1113
- let ty = ry;
1114
- while (tagYs.some(y => Math.abs(y - ty) < 12)) ty += 12;
1115
- tagYs.push(ty);
1116
- for (const t of tickLabels) {
1117
- if (Math.abs(t.y - ty) < 10) t.node.remove();
1118
- }
1119
- el("text", {
1120
- x: AXIS_W - 10, y: ty + 4, "text-anchor": "end", "font-size": 11, "font-weight": 650,
1121
- fill: "var(--fam-reference)",
1122
- }, axisSvg).textContent = fmtMetric(m, r[state.metric]);
1123
- });
1124
- buildLegend(m, shownRefs);
1125
-
1126
- // -- hit targets: one full-height column per method (>= 34px wide)
1127
- const hits = el("g", {}, svg);
1128
- entries.forEach((entry, i) => {
1129
- const h = el("rect", {
1130
- x: i * slot, y: TOP, width: slot, height: PLOT_H + 6, fill: "transparent", cursor: "pointer",
1131
- }, hits);
1132
- h.addEventListener("mouseenter", ev => {
1133
- band.setAttribute("x", i * slot);
1134
- band.setAttribute("opacity", 0.06);
1135
- showTip(entry, ev);
1136
- });
1137
- h.addEventListener("mousemove", ev => tip.move(ev));
1138
- h.addEventListener("mouseleave", () => { band.setAttribute("opacity", 0); tip.hide(); });
1139
- h.addEventListener("click", () => toggleMethod(entry.method));
1140
- });
1141
-
1142
- postHeight();
1143
- }
1144
-
1145
- function showTip(entry, ev) {
1146
- const m = metric();
1147
- let html = `<div class="t-name">${entry.method}</div><div>${entry.family}</div>`;
1148
- for (const p of entry.points) {
1149
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
1150
- const ci = m.ci && p[m.ci.lo] != null
1151
- ? ` <span class="t-var">(${fmtNum(p[m.ci.lo], m.decimals)}–${fmtNum(p[m.ci.hi], m.decimals)})</span>`
1152
- : "";
1153
- html += `<div><span class="t-var">${p.variant}:</span> <b>${fmtMetric(m, p[m.key])}</b>${ci}</div>`;
1154
- }
1155
- if (entry.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(entry.imputed_pct, 0)}% of datasets</div>`;
1156
- tip.show(html, ev);
1157
- }
1158
-
1159
- // ---------- chips ----------
1160
- const chipsBox = document.getElementById("chips");
1161
- const chipByMethod = new Map();
1162
- const famChips = new Map();
1163
-
1164
- function familyMembers(fam) {
1165
- const out = [...byMethod.values()].filter(e => e.family === fam).map(e => e.method);
1166
- for (const r of refs) if (r.family === fam) out.push(r.method);
1167
- return out;
1168
- }
1169
- function isOn(name) { return state.methods.has(name) || state.refs.has(name); }
1170
-
1171
- function buildChips() {
1172
- const rankMetric = metricByKey[CONFIG.rankMetric] || METRICS[0];
1173
- const head = document.createElement("div");
1174
- head.className = "chips-head";
1175
- head.textContent = "Methods shown — click to remove, click a family to toggle the whole group";
1176
- chipsBox.appendChild(head);
1177
- for (const fam of FAM_ORDER) {
1178
- const members = familyMembers(fam);
1179
- if (!members.length) continue;
1180
- members.sort((a, b) => chipRank(a, rankMetric) - chipRank(b, rankMetric));
1181
- const row = document.createElement("div");
1182
- row.className = "chiprow";
1183
- const famBtn = document.createElement("button");
1184
- famBtn.className = "famchip";
1185
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1186
- famBtn.innerHTML = famChipLabel(fam, members.length);
1187
- famBtn.title = `Toggle all ${members.length} ${fam} methods`;
1188
- famBtn.addEventListener("click", () => toggleFamily(fam));
1189
- row.appendChild(famBtn);
1190
- famChips.set(fam, famBtn);
1191
- const set = document.createElement("div");
1192
- set.className = "chipset";
1193
- for (const name of members) {
1194
- const entry = byMethod.get(name);
1195
- const b = document.createElement("button");
1196
- b.className = "chip";
1197
- b.style.setProperty("--fam", FAM_VAR[fam]);
1198
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1199
- b.appendChild(Object.assign(document.createElement("span"), { textContent: name }));
1200
- if (entry && entry.imputed) {
1201
- const mark = document.createElement("span");
1202
- mark.className = "imp-mark";
1203
- mark.textContent = "‡";
1204
- b.appendChild(mark);
1205
- }
1206
- b.title = name + (entry && entry.imputed ? " — partially imputed" : "");
1207
- b.addEventListener("click", () => toggleMethod(name));
1208
- set.appendChild(b);
1209
- chipByMethod.set(name, b);
1210
- }
1211
- row.appendChild(set);
1212
- chipsBox.appendChild(row);
1213
- }
1214
- }
1215
- function chipRank(name, m) {
1216
- const entry = byMethod.get(name);
1217
- if (entry) return rankVal(entry, m);
1218
- const r = refs.find(x => x.method === name);
1219
- const v = r ? r[m.key] : null;
1220
- return v == null ? Infinity : (m.lowerBetter ? v : -v);
1221
- }
1222
- function syncChips() {
1223
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(isOn(name)));
1224
- for (const [fam, b] of famChips) b.setAttribute("aria-pressed", String(familyMembers(fam).every(isOn)));
1225
- }
1226
-
1227
- function toggleMethod(name) {
1228
- const set = byMethod.has(name) ? state.methods : state.refs;
1229
- if (set.has(name)) set.delete(name); else set.add(name);
1230
- syncChips();
1231
- render();
1232
- }
1233
- function toggleFamily(fam) {
1234
- const members = familyMembers(fam);
1235
- const allOn = members.every(isOn);
1236
- for (const name of members) {
1237
- const set = byMethod.has(name) ? state.methods : state.refs;
1238
- if (allOn) set.delete(name); else set.add(name);
1239
- }
1240
- syncChips();
1241
- render();
1242
- }
1243
- function setMethods(names) {
1244
- state.methods = new Set(names);
1245
- syncChips();
1246
- render();
1247
- }
1248
-
1249
- document.getElementById("btn-all").addEventListener("click", () => {
1250
- state.refs = new Set(refs.map(r => r.method));
1251
- setMethods(byMethod.keys());
1252
- });
1253
- document.getElementById("btn-none").addEventListener("click", () => {
1254
- state.refs = new Set();
1255
- setMethods([]);
1256
- });
1257
- document.getElementById("btn-top").addEventListener("click", () => {
1258
- const m = metric();
1259
- // Rank explicitly rather than reusing the display sort: which 15 methods are
1260
- // kept must not depend on which end of the axis the best ones are drawn at
1261
- // (sorting "best on the right" would otherwise select the 15 worst).
1262
- const top = [...byMethod.values()]
1263
- .sort((a, b) => rankVal(a, m) - rankVal(b, m))
1264
- .slice(0, 15)
1265
- .map(e => e.method);
1266
- state.refs = new Set(refs.map(r => r.method));
1267
- setMethods(top);
1268
- });
1269
-
1270
- // ---------- selectors ----------
1271
- yminRange.addEventListener("input", ev => {
1272
- state.yMin = Number(ev.target.value);
1273
- render();
1274
- });
1275
-
1276
- const metricSelect = document.getElementById("metric-select");
1277
- for (const m of METRICS) {
1278
- metricSelect.appendChild(Object.assign(document.createElement("option"), { value: m.key, textContent: m.label }));
1279
- }
1280
- metricSelect.addEventListener("change", ev => {
1281
- state.metric = ev.target.value;
1282
- state.yMin = null; // the previous floor means nothing on a new scale
1283
- buildTable();
1284
- render();
1285
- });
1286
-
1287
- const sortSelect = document.getElementById("sort-select");
1288
- for (const [value, label] of [
1289
- ["best", "Best on the left"], ["worst", "Best on the right"],
1290
- ["family", "Model family"], ["name", "A–Z"],
1291
- ]) {
1292
- sortSelect.appendChild(Object.assign(document.createElement("option"), { value, textContent: label }));
1293
- }
1294
- sortSelect.value = state.sort;
1295
- sortSelect.addEventListener("change", ev => { state.sort = ev.target.value; render(); });
1296
-
1297
- // Variant toggles: buttons rather than chips, since they filter the series
1298
- // rather than the rows. "Default" gets no button at all — every method has a
1299
- // default result, so it is the baseline of the chart rather than an option
1300
- // (switching it off would leave the default-only methods with no bar). Only
1301
- // the extras layered on top, tuning and ensembling, are toggleable; the legend
1302
- // still carries Default's color.
1303
- const ALWAYS_SHOWN = "Default";
1304
- const variantBtns = document.getElementById("variant-btns");
1305
- const variantBtnByKey = new Map();
1306
- for (const v of VARIANT_ORDER) {
1307
- if (v === ALWAYS_SHOWN || !POINTS.some(p => p.variant === v)) continue;
1308
- const b = document.createElement("button");
1309
- b.className = "btn";
1310
- b.textContent = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1311
- b.style.setProperty("--fam", VARIANT_STYLE[v].color);
1312
- b.title = `Show or hide the ${b.textContent.toLowerCase()} bars`;
1313
- b.addEventListener("click", () => {
1314
- if (state.variants.has(v)) state.variants.delete(v);
1315
- else state.variants.add(v);
1316
- syncVariantBtns();
1317
- render();
1318
- });
1319
- variantBtns.appendChild(b);
1320
- variantBtnByKey.set(v, b);
1321
- }
1322
- function syncVariantBtns() {
1323
- for (const [v, b] of variantBtnByKey) {
1324
- const on = state.variants.has(v);
1325
- b.setAttribute("aria-pressed", String(on));
1326
- b.style.opacity = on ? "1" : "0.45";
1327
- b.style.borderColor = on ? VARIANT_STYLE[v].color : "var(--line)";
1328
- }
1329
- }
1330
-
1331
- // ---------- legend ----------
1332
- // Rebuilt on every render: it names the reference lines (each by its dash
1333
- // pattern and current value), which change with the metric and the selection.
1334
- function buildLegend(m, shownRefs) {
1335
- const parts = [];
1336
- for (const v of VARIANT_ORDER) {
1337
- if (!POINTS.some(p => p.variant === v)) continue;
1338
- const label = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1339
- const off = state.variants.has(v) ? "" : ' style="opacity:0.4"';
1340
- parts.push(
1341
- `<span class="item"${off}><svg width="12" height="12" viewBox="0 0 12 12">` +
1342
- `<rect x="1" y="1" width="10" height="10" rx="2" fill="${VARIANT_STYLE[v].color}"/></svg> ${label}</span>`);
1343
- }
1344
- if (m.ci) {
1345
- parts.push('<span class="item"><svg width="12" height="14" viewBox="0 0 12 14">' +
1346
- '<path d="M6,2 V12 M2,2 H10 M2,12 H10" stroke="var(--muted)" stroke-width="1.4" fill="none"/></svg> 95% CI</span>');
1347
- }
1348
- shownRefs.forEach((r, i) => {
1349
- parts.push(`<span class="item"><svg width="26" height="8" viewBox="0 0 26 8">` +
1350
- `<line x1="0" y1="4" x2="26" y2="4" stroke="var(--fam-reference)" stroke-width="1.8" ` +
1351
- `stroke-dasharray="${REF_DASHES[i % REF_DASHES.length]}"/></svg> ${r.method} · ${fmtMetric(m, r[state.metric])}</span>`);
1352
- });
1353
- if (POINTS.some(p => p.imputed)) {
1354
- parts.push('<span class="item"><svg width="14" height="14" viewBox="0 0 14 14">' +
1355
- '<rect x="1" y="1" width="12" height="12" rx="2" fill="var(--pt-muted)"/>' +
1356
- '<path d="M1,1 L7,7 M7,1 L1,7 M7,7 L13,13 M13,7 L7,13 M1,7 L7,13 M7,7 L13,1" ' +
1357
- 'stroke="var(--paper)" stroke-width="1.5" fill="none"/>' +
1358
- "</svg> &Dagger; partially imputed</span>");
1359
- }
1360
- // Model family, named rather than merely pointed at: without the colors
1361
- // spelled out, the swatch under each column decodes to nothing.
1362
- const families = FAM_ORDER.filter(f => POINTS.some(p => !p.reference && p.family === f));
1363
- if (families.length > 1) {
1364
- parts.push('<span class="legendbreak"></span><span class="item">Family:</span>');
1365
- for (const fam of families) {
1366
- parts.push(`<span class="item"><svg width="16" height="9" viewBox="0 0 16 9">` +
1367
- `<rect x="0" y="1" width="16" height="7" rx="2" fill="${FAM_VAR[fam]}"/></svg> ` +
1368
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`);
1369
- }
1370
- }
1371
- document.getElementById("legendstrip").innerHTML = parts.join("");
1372
- }
1373
-
1374
- // ---------- data table (the WCAG-clean twin of the chart) ----------
1375
- function buildTable() {
1376
- const m = metric();
1377
- const rows = [...POINTS].filter(p => p[m.key] != null).sort((a, b) =>
1378
- m.lowerBetter ? a[m.key] - b[m.key] : b[m.key] - a[m.key]);
1379
- let html = "<table><thead><tr><th>Method</th><th>Variant</th><th>Family</th>";
1380
- for (const x of METRICS) html += `<th>${x.label}</th>`;
1381
- html += "<th>Imputed</th></tr></thead><tbody>";
1382
- for (const p of rows) {
1383
- html += `<tr><td>${p.method}</td><td>${p.variant || "—"}</td><td>${p.family}</td>`;
1384
- for (const x of METRICS) html += `<td>${fmtMetric(x, p[x.key])}</td>`;
1385
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1386
- }
1387
- document.getElementById("tblwrap").innerHTML = html + "</tbody></table>";
1388
- }
1389
-
1390
- // ---------- paper view ----------
1391
- setUpPaperView(render);
1392
- // The y-axis lives in its own pane, so it is offset back into place.
1393
- setUpExport(() => [{ svg: axisSvg, dx: 0 }, { svg: svg, dx: AXIS_W }], () => slugify(document.title));
1394
-
1395
- // ---------- boot ----------
1396
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1397
- let resizeTimer = null;
1398
- window.addEventListener("resize", () => {
1399
- clearTimeout(resizeTimer);
1400
- resizeTimer = setTimeout(render, 120);
1401
- });
1402
-
1403
- buildChips();
1404
- buildTable();
1405
- syncChips();
1406
- syncVariantBtns();
1407
- render(); // also builds the legend (it depends on the metric + selection)
1408
- })();
1409
- </script>
1410
- </body>
1411
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/leaderboard_table.html DELETED
@@ -1,1162 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard table — no imputation | All Tasks | All Datasets</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* --- The table ------------------------------------------------------------
269
- A scroll box with a sticky header, so the column names stay put through 80
270
- rows. Numbers are tabular so digits line up down a column. */
271
- .lbt-scroll {
272
- overflow: auto; max-height: 720px; margin-top: 4px;
273
- border: 1px solid var(--line); border-radius: 10px;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lbt-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
277
- .lbt-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- table.lbt { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
282
- table.lbt th, table.lbt td { padding: 5px 9px; text-align: center; border: 1px solid var(--line); }
283
- table.lbt thead th {
284
- position: sticky; top: 0; z-index: 3; background: var(--card); font-size: 12px;
285
- font-weight: 650; white-space: nowrap; cursor: pointer; user-select: none;
286
- box-shadow: inset 0 -1px 0 var(--line);
287
- }
288
- /* The header is both the sort control and the definition, so it gets the
289
- pointer of the former and the dotted underline of the latter. */
290
- table.lbt thead th .lbl { text-decoration: underline; text-decoration-style: dotted;
291
- text-decoration-color: var(--muted); text-underline-offset: 3px; }
292
- table.lbt thead th:hover { color: var(--accent); }
293
- table.lbt thead th::after { content: "\2195"; font-size: 0.72em; opacity: 0.3; margin-left: 5px; }
294
- table.lbt thead th[aria-sort="ascending"]::after { content: "\25B2"; opacity: 0.85; }
295
- table.lbt thead th[aria-sort="descending"]::after { content: "\25BC"; opacity: 0.85; }
296
- table.lbt td.name { text-align: left; white-space: nowrap; }
297
- table.lbt td.pos { color: var(--muted); font-variant-numeric: tabular-nums; }
298
- table.lbt td.type { white-space: nowrap; width: 1%; }
299
- table.lbt td.num { font-weight: 600; }
300
- table.lbt td.na { color: var(--pt-muted); }
301
- table.lbt tbody tr:hover td { filter: brightness(1.12); }
302
- .ci { opacity: 0.5; font-weight: 400; font-size: 0.82em; }
303
- .pill { padding: 1px 7px; border-radius: 999px; font-size: 0.95em; white-space: nowrap; }
304
- .verified { font-size: 0.85em; }
305
- .link-icon { font-size: 0.78em; opacity: 0.65; margin-left: 2px; }
306
- /* Grey rather than a dimmed inherit: dimming leaves the tag tinted with the
307
- family colour, which the model name already carries, and made this table
308
- disagree with the cross-subset table on the app page. */
309
- .variant-tag { color: var(--muted); font-weight: 400; font-size: 0.9em; }
310
- td.name .imp-mark { color: var(--muted); font-weight: 700; margin-left: 3px; }
311
- td.name a { text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
312
- td.name a:hover { text-decoration-style: solid; }
313
-
314
- .lbt-search {
315
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
316
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
317
- padding: 6px 9px; width: 12em;
318
- }
319
- .lbt-cap { font-size: 12.5px; color: var(--muted); margin: 7px 0 0; }
320
- .lbt-empty { padding: 34px 0; text-align: center; color: var(--muted); font-size: 13px; }
321
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
322
- /* The model selector sits above the table it filters. Families pack side by
323
- side rather than stacking: one row each would leave the small ones (Baseline,
324
- Other) wasting a line. */
325
- .chips { margin: 2px 0 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
326
- .chiprow { flex: 0 1 auto; min-width: 0; }
327
-
328
- /* The data export, in the same green the website's figure-export controls use,
329
- so "this downloads something" reads the same across the site. */
330
- .btn.export {
331
- color: #063; background: #7ee0b8; border-color: #7ee0b8; font-weight: 700;
332
- }
333
- .btn.export:hover { background: #a6efcd; border-color: #a6efcd; }
334
- /* Paper view keeps the table: here the table *is* the figure. */
335
- body.paper .lbt-scroll { max-height: none; }
336
- </style>
337
- </head>
338
- <body>
339
- <div class="viewbar">
340
- <button class="btn" id="btn-paper" title="White background, table only — for slides and papers">Paper view</button>
341
- </div>
342
- <p class="explorer-title" id="title"></p>
343
- <div class="controls">
344
- <span class="grouplabel">Variants</span>
345
- <div class="btnrow" id="variant-btns"></div>
346
- <button class="btn" id="btn-imputed" title="Models whose score is partly imputed, marked &#8225;"></button>
347
- <input class="lbt-search" id="search" type="search" placeholder="Search model or family" aria-label="Search">
348
- <span class="grouplabel">Columns</span>
349
- <div class="btnrow" id="col-btns"></div>
350
- <button class="btn export" id="btn-csv" title="Download the rows and columns shown, in the current sort order">Download CSV</button>
351
- </div>
352
- <!-- The shared paper-view helper reveals this bar; a table has no figure to
353
- export as SVG/PNG/PDF, and its CSV button belongs beside the filters that
354
- shape the export, so it stays empty here. -->
355
- <div class="exportbar" id="exportbar" hidden></div>
356
- <!-- The model selector belongs above the table it filters. It doubles as the
357
- family key, which is why there is no separate legend strip. -->
358
- <div class="chips" id="chips"></div>
359
- <div class="lbt-scroll" id="tblwrap"></div>
360
- <p class="lbt-cap" id="caption"></p>
361
-
362
- <script>
363
- (function () {
364
- "use strict";
365
- const CONFIG = {"title": null, "columns": [{"key": "position", "label": "#", "always": true, "hint": "Position in this subset's published ranking."}, {"key": "family", "label": "Type", "always": true, "hint": "Model family \u2014 see the key above the table."}, {"key": "model", "label": "Model", "always": true, "hint": "The model, its tuning variant in brackets, \u2714\ufe0f when the implementation was verified, and \u2021 when some results are imputed."}, {"key": "elo", "label": "Elo", "decimals": 0, "heatmap": true, "hint": "Pairwise win-rate rating; a 400-point gap is about a 91% win rate. Higher is better."}, {"key": "score", "label": "Score", "decimals": 3, "heatmap": true, "hint": "Error rescaled per dataset to 1 (best) \u2026 0 (median), then averaged. Higher is better."}, {"key": "rank", "label": "Rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Mean rank across datasets. Lower is better."}, {"key": "hrank", "label": "Harmonic rank", "short": "H. rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Harmonic mean of per-dataset ranks; rewards being excellent somewhere. Lower is better."}, {"key": "imp", "label": "Improvability (%)", "short": "Improv.", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "How much lower the best model's error is than this one's, per dataset. Lower is better."}, {"key": "train_time", "label": "Train time (s/1K)", "short": "Train s", "decimals": 2, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to fit per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "predict_time", "label": "Predict time (s/1K)", "short": "Predict s", "decimals": 3, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to predict per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "imputed_pct", "label": "Imputed (%)", "short": "Imputed", "decimals": 1, "lowerBetter": true, "heatmap": true, "hint": "Share of datasets whose score was imputed because the model could not run on them."}, {"key": "hardware", "label": "Hardware", "text": true, "hint": "The hardware the reported runtimes were measured on."}], "rankKey": "elo", "variants": ["Default", "Tuned", "Tuned + Ens."]};
366
- const POINTS = [{"position":0,"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1792.0,"score":0.889,"rank":6.17,"hrank":1.72,"imp":4.074,"train_time":38.81,"predict_time":6.985,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+115\/-110","imputed":false},{"position":1,"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1691.0,"score":0.662,"rank":9.39,"hrank":4.03,"imp":7.979,"train_time":289.07,"predict_time":4.031,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+82\/-68","imputed":false},{"position":2,"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1665.0,"score":0.67,"rank":10.41,"hrank":4.06,"imp":9.328,"train_time":3.66,"predict_time":0.399,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+80\/-61","imputed":false},{"position":3,"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1620.0,"score":0.583,"rank":12.42,"hrank":7.14,"imp":10.751,"train_time":5.48,"predict_time":0.555,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+71\/-51","imputed":false},{"position":4,"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1595.0,"score":0.549,"rank":13.64,"hrank":6.22,"imp":10.427,"train_time":2040.22,"predict_time":8.908,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-56","imputed":false},{"position":5,"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1594.0,"score":0.606,"rank":13.71,"hrank":4.97,"imp":10.175,"train_time":2.05,"predict_time":0.151,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+73\/-61","imputed":false},{"position":6,"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1554.0,"score":0.504,"rank":15.81,"hrank":7.33,"imp":11.116,"train_time":2040.22,"predict_time":1.218,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+65\/-55","imputed":false},{"position":7,"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1524.0,"score":0.474,"rank":17.55,"hrank":9.62,"imp":11.66,"train_time":5.81,"predict_time":0.645,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+62\/-44","imputed":false},{"position":8,"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1514.0,"score":0.401,"rank":18.2,"hrank":8.44,"imp":12.789,"train_time":1735.72,"predict_time":2.558,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+53\/-46","imputed":false},{"position":9,"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1508.0,"score":0.405,"rank":18.55,"hrank":11.52,"imp":12.881,"train_time":2950.72,"predict_time":11.975,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+54\/-46","imputed":false},{"position":10,"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1459.0,"score":0.38,"rank":21.7,"hrank":9.59,"imp":14.0,"train_time":2.07,"predict_time":0.182,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+55\/-51","imputed":false},{"position":11,"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1458.0,"score":0.375,"rank":21.81,"hrank":8.16,"imp":13.788,"train_time":4910.38,"predict_time":286.537,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+60\/-51","imputed":false},{"position":12,"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1450.0,"score":0.315,"rank":22.33,"hrank":12.48,"imp":14.081,"train_time":2450.13,"predict_time":2.247,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+52\/-43","imputed":false},{"position":13,"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1433.0,"score":0.262,"rank":23.53,"hrank":17.2,"imp":15.033,"train_time":417.05,"predict_time":2.639,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+33\/-31","imputed":false},{"position":14,"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1426.0,"score":0.295,"rank":23.99,"hrank":13.67,"imp":14.202,"train_time":2950.72,"predict_time":0.663,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+48\/-45","imputed":false},{"position":15,"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1422.0,"score":0.261,"rank":24.34,"hrank":14.77,"imp":14.656,"train_time":1346.21,"predict_time":0.344,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+39\/-38","imputed":false},{"position":16,"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1408.0,"score":0.245,"rank":25.3,"hrank":15.31,"imp":14.885,"train_time":1346.21,"predict_time":0.038,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+38\/-43","imputed":false},{"position":17,"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1404.0,"score":0.235,"rank":25.56,"hrank":17.78,"imp":15.304,"train_time":12685.08,"predict_time":464.37,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+40\/-42","imputed":false},{"position":18,"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1403.0,"score":0.316,"rank":25.69,"hrank":11.3,"imp":14.936,"train_time":4910.38,"predict_time":39.96,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+59\/-56","imputed":false},{"position":19,"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1394.0,"score":0.251,"rank":26.35,"hrank":14.47,"imp":14.952,"train_time":2450.13,"predict_time":0.251,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+48\/-42","imputed":false},{"position":20,"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1389.0,"score":0.277,"rank":26.66,"hrank":12.79,"imp":15.599,"train_time":4621.67,"predict_time":8.138,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+73\/-52","imputed":false},{"position":21,"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1386.0,"score":0.194,"rank":26.93,"hrank":20.71,"imp":15.688,"train_time":417.05,"predict_time":0.334,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+29\/-29","imputed":false},{"position":22,"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1378.0,"score":0.224,"rank":27.47,"hrank":16.11,"imp":15.524,"train_time":5.88,"predict_time":0.025,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+40\/-42","imputed":false},{"position":23,"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1375.0,"score":0.198,"rank":27.73,"hrank":19.98,"imp":15.754,"train_time":693.49,"predict_time":1.689,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+31\/-33","imputed":false},{"position":24,"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1363.0,"score":0.299,"rank":28.66,"hrank":9.55,"imp":15.526,"train_time":26.46,"predict_time":6.242,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+78\/-59","imputed":false},{"position":25,"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1355.0,"score":0.189,"rank":29.21,"hrank":16.82,"imp":16.044,"train_time":4621.67,"predict_time":0.475,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+37\/-37","imputed":false},{"position":26,"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1350.0,"score":0.251,"rank":29.58,"hrank":17.68,"imp":16.081,"train_time":1.18,"predict_time":0.072,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+58\/-52","imputed":false},{"position":27,"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1350.0,"score":0.16,"rank":29.6,"hrank":22.22,"imp":16.065,"train_time":693.49,"predict_time":0.308,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+29\/-32","imputed":false},{"position":28,"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1348.0,"score":0.209,"rank":29.79,"hrank":18.96,"imp":16.284,"train_time":846.89,"predict_time":2.549,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+49\/-43","imputed":false},{"position":29,"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1333.0,"score":0.186,"rank":30.87,"hrank":19.34,"imp":17.213,"train_time":427.39,"predict_time":0.404,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+46\/-60","imputed":false},{"position":30,"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1325.0,"score":0.245,"rank":31.53,"hrank":14.33,"imp":17.225,"train_time":47.65,"predict_time":43.738,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+64\/-55","imputed":false},{"position":31,"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1308.0,"score":0.156,"rank":32.79,"hrank":21.32,"imp":17.551,"train_time":427.39,"predict_time":0.034,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+44\/-60","imputed":false},{"position":32,"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1300.0,"score":0.158,"rank":33.43,"hrank":22.28,"imp":17.449,"train_time":846.89,"predict_time":0.13,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+45\/-39","imputed":false},{"position":33,"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1298.0,"score":0.161,"rank":33.63,"hrank":21.84,"imp":17.0,"train_time":7.49,"predict_time":0.244,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+47\/-44","imputed":false},{"position":34,"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1295.0,"score":0.111,"rank":33.83,"hrank":23.47,"imp":17.147,"train_time":12685.08,"predict_time":62.13,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+40\/-33","imputed":false},{"position":35,"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1285.0,"score":0.126,"rank":34.58,"hrank":25.37,"imp":17.161,"train_time":2875.52,"predict_time":1.952,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+48\/-47","imputed":false},{"position":36,"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1283.0,"score":0.194,"rank":34.72,"hrank":14.11,"imp":18.278,"train_time":14.11,"predict_time":2.075,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+57\/-55","imputed":false},{"position":37,"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1272.0,"score":0.116,"rank":35.64,"hrank":25.8,"imp":18.672,"train_time":1711.25,"predict_time":0.141,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+40\/-40","imputed":false},{"position":38,"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1264.0,"score":0.121,"rank":36.23,"hrank":23.23,"imp":18.657,"train_time":1.99,"predict_time":0.033,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+47\/-55","imputed":false},{"position":39,"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1248.0,"score":0.09,"rank":37.44,"hrank":23.68,"imp":19.194,"train_time":14.87,"predict_time":0.307,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+41\/-39","imputed":false},{"position":40,"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1235.0,"score":0.082,"rank":38.43,"hrank":28.17,"imp":19.314,"train_time":1711.25,"predict_time":0.015,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+44\/-40","imputed":false},{"position":41,"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1230.0,"score":0.066,"rank":38.85,"hrank":29.23,"imp":18.569,"train_time":10.06,"predict_time":1.689,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+36\/-37","imputed":false},{"position":42,"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1214.0,"score":0.053,"rank":40.04,"hrank":30.76,"imp":18.792,"train_time":1.94,"predict_time":0.123,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+39\/-36","imputed":false},{"position":43,"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1212.0,"score":0.069,"rank":40.24,"hrank":30.96,"imp":18.877,"train_time":2875.52,"predict_time":0.131,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+41\/-42","imputed":false},{"position":44,"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1208.0,"score":0.083,"rank":40.47,"hrank":28.16,"imp":19.777,"train_time":263.04,"predict_time":0.766,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+49\/-43","imputed":false},{"position":45,"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1207.0,"score":0.105,"rank":40.58,"hrank":25.38,"imp":19.677,"train_time":593.24,"predict_time":4.467,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-59","imputed":false},{"position":46,"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1204.0,"score":0.069,"rank":40.82,"hrank":28.54,"imp":20.255,"train_time":6.67,"predict_time":0.014,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+45\/-49","imputed":false},{"position":47,"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1186.0,"score":0.038,"rank":42.12,"hrank":36.15,"imp":19.344,"train_time":1.96,"predict_time":0.142,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+34\/-29","imputed":false},{"position":48,"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1180.0,"score":0.074,"rank":42.56,"hrank":26.73,"imp":20.564,"train_time":263.04,"predict_time":0.071,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+45\/-52","imputed":false},{"position":49,"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1171.0,"score":0.06,"rank":43.28,"hrank":29.04,"imp":20.828,"train_time":373.24,"predict_time":0.771,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+54\/-47","imputed":false},{"position":50,"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1147.0,"score":0.048,"rank":45.01,"hrank":31.19,"imp":21.096,"train_time":593.24,"predict_time":0.306,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+52\/-59","imputed":false},{"position":51,"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1137.0,"score":0.041,"rank":45.71,"hrank":28.56,"imp":21.535,"train_time":373.24,"predict_time":0.085,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+50\/-44","imputed":false},{"position":52,"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1097.0,"score":0.125,"rank":48.47,"hrank":15.95,"imp":25.584,"train_time":28729.74,"predict_time":4.268,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+75\/-83","imputed":false},{"position":53,"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1096.0,"score":0.123,"rank":48.56,"hrank":15.35,"imp":25.6,"train_time":28729.74,"predict_time":18.811,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+79\/-83","imputed":false},{"position":54,"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1094.0,"score":0.037,"rank":48.66,"hrank":33.93,"imp":23.024,"train_time":296.64,"predict_time":68.173,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+47\/-54","imputed":false},{"position":55,"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1089.0,"score":0.024,"rank":49.04,"hrank":38.83,"imp":26.446,"train_time":185.31,"predict_time":0.6,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+44\/-45","imputed":false},{"position":56,"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1074.0,"score":0.011,"rank":49.99,"hrank":39.42,"imp":22.815,"train_time":9.99,"predict_time":0.126,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+34\/-42","imputed":false},{"position":57,"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1050.0,"score":0.011,"rank":51.52,"hrank":39.96,"imp":27.802,"train_time":185.31,"predict_time":0.258,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+47\/-50","imputed":false},{"position":58,"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1043.0,"score":0.029,"rank":51.95,"hrank":44.11,"imp":26.445,"train_time":3.23,"predict_time":0.919,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+61\/-70","imputed":false},{"position":59,"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1009.0,"score":0.012,"rank":54.0,"hrank":47.38,"imp":26.176,"train_time":1.87,"predict_time":0.283,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-66","imputed":false},{"position":60,"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1008.0,"score":0.009,"rank":54.07,"hrank":39.97,"imp":25.682,"train_time":2.86,"predict_time":0.373,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+57\/-62","imputed":false},{"position":61,"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1000.0,"score":0.003,"rank":54.51,"hrank":49.81,"imp":26.265,"train_time":0.43,"predict_time":0.054,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+47\/-47","imputed":false},{"position":62,"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":997.0,"score":0.017,"rank":54.67,"hrank":38.92,"imp":27.822,"train_time":129.08,"predict_time":1.801,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+61\/-76","imputed":false},{"position":63,"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":993.0,"score":0.077,"rank":54.89,"hrank":24.31,"imp":30.456,"train_time":384.75,"predict_time":5.348,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+89\/-104","imputed":false},{"position":64,"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":965.0,"score":0.031,"rank":56.39,"hrank":40.87,"imp":33.667,"train_time":237.63,"predict_time":0.424,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+64\/-100","imputed":false},{"position":65,"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":940.0,"score":0.022,"rank":57.67,"hrank":42.64,"imp":34.211,"train_time":237.63,"predict_time":0.085,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+70\/-105","imputed":false},{"position":66,"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":936.0,"score":0.0,"rank":57.84,"hrank":45.44,"imp":31.748,"train_time":27.32,"predict_time":0.029,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+43\/-57","imputed":false},{"position":67,"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":890.0,"score":0.008,"rank":59.92,"hrank":46.93,"imp":32.782,"train_time":129.08,"predict_time":0.18,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+61\/-100","imputed":false},{"position":68,"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":861.0,"score":0.014,"rank":61.12,"hrank":47.51,"imp":36.822,"train_time":1.19,"predict_time":0.12,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+69\/-118","imputed":false},{"position":69,"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":654.0,"score":0.0,"rank":66.89,"hrank":60.42,"imp":45.987,"train_time":0.19,"predict_time":0.036,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+78\/-91","imputed":false}];
367
-
368
-
369
- const NS = "http://www.w3.org/2000/svg";
370
- // Baseline and Other are one bucket, as in the site's own type legend: they
371
- // already share a color, and each holds only a handful of methods.
372
- const FAM_MERGED = "Baseline / Other";
373
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
374
- // Normalized up front so every later lookup — colors, chips, sorting — sees
375
- // the merged family. Both templates declare POINTS above this block.
376
- for (const p of POINTS) p.family = famOf(p.family);
377
-
378
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
379
- const FAM_VAR = {
380
- "Foundation Model": "var(--fam-foundation)",
381
- "Tree-based": "var(--fam-tree)",
382
- "Neural Network": "var(--fam-nn)",
383
- "Reference Pipeline": "var(--fam-reference)",
384
- [FAM_MERGED]: "var(--fam-baseline)",
385
- };
386
- // The symbol the website shows for each family, so a family chip here reads the
387
- // same as the Type column on the site. Baseline and Other are one bucket, so
388
- // that chip carries both symbols.
389
- const FAM_SYMBOL = {
390
- "Foundation Model": "🧠⚡",
391
- "Tree-based": "🌳",
392
- "Neural Network": "🧠🔁",
393
- "Reference Pipeline": "📊",
394
- [FAM_MERGED]: "📏 ❓",
395
- };
396
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
397
- const VARIANT_VAR = {
398
- "Default": "var(--var-default)",
399
- "Tuned": "var(--var-tuned)",
400
- "Tuned + Ens.": "var(--var-tunedens)",
401
- };
402
-
403
- // A family chip's label: its symbol, its name and how many methods it holds.
404
- function famChipLabel(family, count) {
405
- const symbol = FAM_SYMBOL[family];
406
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
407
- family + ' <span class="count">&times;' + count + "</span>";
408
- }
409
-
410
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
411
- const FAM_INK = {
412
- "Foundation Model": "var(--fam-foundation-ink)",
413
- "Tree-based": "var(--fam-tree-ink)",
414
- "Neural Network": "var(--fam-nn-ink)",
415
- "Reference Pipeline": "var(--fam-reference-ink)",
416
- [FAM_MERGED]: "var(--fam-baseline-ink)",
417
- };
418
-
419
- // Create an SVG element with attributes, optionally appended to `parent`.
420
- function el(name, attrs, parent) {
421
- const node = document.createElementNS(NS, name);
422
- for (const k in attrs) node.setAttribute(k, attrs[k]);
423
- if (parent) parent.appendChild(node);
424
- return node;
425
- }
426
-
427
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
428
- // locale-independent by definition, which is the point: `toLocaleString`
429
- // would follow the *viewer's* browser locale and print 1234,5 for a German
430
- // visitor, disagreeing with the figures and CSVs beside it.
431
- function fmtNum(v, decimals) {
432
- if (v == null || !isFinite(v)) return "—";
433
- return v.toFixed(decimals);
434
- }
435
-
436
- function fmtMetric(metric, v) {
437
- if (v == null || !isFinite(v)) return "—";
438
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
439
- }
440
-
441
- function fmtTime(v) {
442
- if (v >= 100) return fmtNum(v, 0) + " s";
443
- if (v >= 1) return fmtNum(v, 1) + " s";
444
- if (v >= 0.1) return fmtNum(v, 2) + " s";
445
- return fmtNum(v, 3) + " s";
446
- }
447
-
448
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
449
- function niceStep(raw) {
450
- if (!(raw > 0)) return 1;
451
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
452
- for (const m of [1, 2, 2.5, 5, 10]) {
453
- if (mag * m >= raw) return mag * m;
454
- }
455
- return mag * 10;
456
- }
457
-
458
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
459
- // index rather than by accumulation so fractional steps do not drift.
460
- function ticks(min, max, target) {
461
- const step = niceStep((max - min) / target);
462
- const first = Math.ceil(min / step);
463
- const out = [];
464
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
465
- return out.length ? out : [min];
466
- }
467
-
468
- // A hover tooltip anchored inside `box` (which must be position:relative).
469
- function makeTooltip(box) {
470
- const node = box.querySelector(".tooltip");
471
- return {
472
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
473
- move(ev) {
474
- const r = box.getBoundingClientRect();
475
- let tx = ev.clientX - r.left + 14;
476
- const ty = ev.clientY - r.top + 12;
477
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
478
- node.style.left = tx + "px";
479
- node.style.top = ty + "px";
480
- },
481
- hide() { node.style.display = "none"; },
482
- };
483
- }
484
-
485
- // Paper view — white surface, legend + chart only — is the *default*: what a
486
- // reader wants first is the figure, and it is the state worth exporting. The
487
- // controls, chip list and data table are one click away behind "Edit view".
488
- // `afterToggle` re-renders charts whose size is measured from the layout.
489
- // `options.openInPaper` (default true) decides the state the page opens in. A
490
- // chart opens as the figure; the leaderboard table opens with its controls,
491
- // since there the interaction is the point rather than scaffolding around it.
492
- function setUpPaperView(afterToggle, options) {
493
- const opts = options || {};
494
- const root = document.documentElement;
495
- let hostTheme = null; // the embedding page's choice, captured on entry
496
- const btn = document.getElementById("btn-paper");
497
- const embedded = window.parent !== window;
498
-
499
- function setPaper(on) {
500
- document.body.classList.toggle("paper", on);
501
- if (on) {
502
- hostTheme = root.getAttribute("data-theme");
503
- root.setAttribute("data-theme", "light");
504
- } else if (hostTheme) {
505
- root.setAttribute("data-theme", hostTheme);
506
- } else {
507
- root.removeAttribute("data-theme");
508
- }
509
- btn.textContent = on ? "Edit view" : "Paper view";
510
- document.getElementById("exportbar").hidden = !on || embedded;
511
- if (afterToggle) requestAnimationFrame(afterToggle);
512
- postHeight();
513
- }
514
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
515
- // Embedded, the host page owns these controls — they sit beside the panel's
516
- // static-figure toggle and are driven from the outside. Standalone (the
517
- // shareable single file) this page needs its own.
518
- if (embedded) document.querySelector(".viewbar").hidden = true;
519
- window.addEventListener("message", ev => {
520
- const d = ev.data;
521
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
522
- });
523
- // Only standalone: embedded, the host owns the button and would not see the
524
- // key press, so its label would fall out of step with the frame.
525
- if (!embedded) {
526
- document.addEventListener("keydown", ev => {
527
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
528
- });
529
- }
530
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
531
- }
532
-
533
- // --- Figure export ---------------------------------------------------------
534
- // The chart is live SVG, so a file can be built from it directly. Three things
535
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
536
- // nothing outside this document), it has no background or font of its own, and
537
- // the legend is HTML rather than part of the SVG.
538
-
539
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
540
- // cloned and its label re-emitted at the measured position. foreignObject would
541
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
542
- // break the PNG path.
543
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
544
- // once the node leaves this document.
545
- function resolveVars(root, resolve) {
546
- for (const node of [root, ...root.querySelectorAll("*")]) {
547
- for (const attr of ["fill", "stroke"]) {
548
- const value = node.getAttribute(attr);
549
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
550
- }
551
- }
552
- }
553
-
554
- function legendToSvg(container, resolve) {
555
- const base = container.getBoundingClientRect();
556
- const group = document.createElementNS(NS, "g");
557
- let height = 0;
558
- for (const item of container.querySelectorAll(".item")) {
559
- const box = item.getBoundingClientRect();
560
- if (!box.width) continue;
561
- height = Math.max(height, box.bottom - base.top);
562
- let textLeft = box.left - base.left;
563
- const glyph = item.querySelector("svg");
564
- if (glyph) {
565
- const gbox = glyph.getBoundingClientRect();
566
- const wrap = el("g", {
567
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
568
- }, group);
569
- const glyphClone = glyph.cloneNode(true);
570
- resolveVars(glyphClone, resolve);
571
- wrap.appendChild(glyphClone);
572
- textLeft = gbox.right - base.left + 5;
573
- }
574
- const label = item.textContent.trim();
575
- if (!label) continue;
576
- const colored = item.querySelector("[style*='color']");
577
- const text = el("text", {
578
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
579
- fill: resolve(getComputedStyle(colored || item).color),
580
- }, group);
581
- text.textContent = label;
582
- }
583
- return { group, height: Math.ceil(height) };
584
- }
585
-
586
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
587
- // y-axis beside the scrolling plot) still exports as one figure.
588
- function buildExportSvg(parts, legendEl, pad = 10) {
589
- const rootStyle = getComputedStyle(document.documentElement);
590
- const resolve = value => String(value).replace(
591
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
592
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
593
-
594
- let chartW = 0, chartH = 0;
595
- for (const part of parts) {
596
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
597
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
598
- }
599
-
600
- const out = document.createElementNS(NS, "svg");
601
- out.setAttribute("xmlns", NS);
602
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
603
- let top = pad;
604
- const later = []; // built after the width is known
605
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
606
- if (legend && legend.height) {
607
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
608
- later.push(() => out.appendChild(legend.group));
609
- top += legend.height + 8;
610
- }
611
-
612
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
613
- const height = top + chartH + pad;
614
- out.setAttribute("width", Math.ceil(width));
615
- out.setAttribute("height", Math.ceil(height));
616
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
617
- for (const build of later) build();
618
-
619
- for (const part of parts) {
620
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
621
- const clone = part.svg.cloneNode(true);
622
- resolveVars(clone, resolve);
623
- while (clone.firstChild) group.appendChild(clone.firstChild);
624
- }
625
- return out;
626
- }
627
-
628
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
629
- function slugify(text) {
630
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
631
- }
632
-
633
- function downloadUrl(url, filename) {
634
- const a = document.createElement("a");
635
- a.href = url;
636
- a.download = filename;
637
- document.body.appendChild(a);
638
- a.click();
639
- a.remove();
640
- }
641
-
642
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
643
- function rasterize(svg, scale, done, fail) {
644
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
645
- const img = new Image();
646
- img.onload = () => {
647
- const canvas = document.createElement("canvas");
648
- canvas.width = Math.round(width * scale);
649
- canvas.height = Math.round(height * scale);
650
- const ctx = canvas.getContext("2d");
651
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
652
- ctx.drawImage(img, 0, 0);
653
- done(canvas, width, height);
654
- };
655
- img.onerror = fail;
656
- img.src = "data:image/svg+xml;charset=utf-8,"
657
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
658
- }
659
-
660
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
661
- // cost this page its zero-dependency, single-file property. The image is stored
662
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
663
- // in points to the figure's CSS size, so it prints at the size it appears here
664
- // and the pixels land at 96*scale dpi.
665
- async function buildPdf(canvas, cssWidth, cssHeight) {
666
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
667
- const rgb = new Uint8Array((pixels.length / 4) * 3);
668
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
669
- rgb[j] = pixels[i];
670
- rgb[j + 1] = pixels[i + 1];
671
- rgb[j + 2] = pixels[i + 2];
672
- }
673
- const deflated = new Uint8Array(await new Response(
674
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
675
-
676
- const encoder = new TextEncoder();
677
- const chunks = [];
678
- const offsets = [];
679
- let cursor = 0;
680
- const put = data => {
681
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
682
- chunks.push(bytes);
683
- cursor += bytes.length;
684
- };
685
- const object = (id, body, stream) => {
686
- offsets[id] = cursor;
687
- put(`${id} 0 obj\n${body}\n`);
688
- if (stream) {
689
- put("stream\n");
690
- put(stream);
691
- put("\nendstream\n");
692
- }
693
- put("endobj\n");
694
- };
695
-
696
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
697
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
698
- put("%PDF-1.4\n");
699
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
700
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
701
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
702
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
703
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
704
- object(4, "<< /Type /XObject /Subtype /Image "
705
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
706
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
707
- object(5, `<< /Length ${content.length} >>`, content);
708
-
709
- const xref = cursor;
710
- let table = "xref\n0 6\n0000000000 65535 f \n";
711
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
712
- put(table);
713
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
714
- return new Blob(chunks, { type: "application/pdf" });
715
- }
716
-
717
- // Wire up the export buttons; `getParts` is called per click so the file always
718
- // matches what is on screen. Returns a `run(format)` the host page can drive.
719
- function setUpExport(getParts, basename) {
720
- const buttons = {
721
- svg: document.getElementById("btn-svg"),
722
- png: document.getElementById("btn-png"),
723
- pdf: document.getElementById("btn-pdf"),
724
- };
725
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
726
-
727
- // A sandboxed frame has no modals, so a failure is reported on the button.
728
- function complain(format) {
729
- const button = buttons[format];
730
- if (!button) return;
731
- const label = button.textContent;
732
- button.textContent = "failed";
733
- setTimeout(() => { button.textContent = label; }, 2500);
734
- }
735
-
736
- function run(format) {
737
- const svg = figure();
738
- const name = basename();
739
- if (format === "svg") {
740
- downloadUrl("data:image/svg+xml;charset=utf-8,"
741
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
742
- return;
743
- }
744
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
745
- // points so the pixels already land near 200 dpi at print size.
746
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
747
- if (format === "png") {
748
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
749
- } else {
750
- buildPdf(canvas, cssWidth, cssHeight)
751
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
752
- .catch(() => complain("pdf"));
753
- }
754
- }, () => complain(format));
755
- }
756
-
757
- for (const format of Object.keys(buttons)) {
758
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
759
- }
760
- // Embedded, the buttons live in the host's panel header (see main.taExport).
761
- window.addEventListener("message", ev => {
762
- const d = ev.data;
763
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
764
- });
765
- }
766
-
767
- // When embedded, report the content height so the host page can size the
768
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
769
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
770
- // clamped to at least the iframe's current viewport, which turns the
771
- // resize round-trip into a grow-forever feedback loop. The change guard
772
- // stops re-posting once the height settles.
773
- let lastPostedHeight = 0;
774
- function postHeight() {
775
- if (window.parent === window) return;
776
- const height = Math.ceil(document.body.offsetHeight);
777
- if (Math.abs(height - lastPostedHeight) < 3) return;
778
- lastPostedHeight = height;
779
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
780
- }
781
-
782
-
783
- const COLUMNS = CONFIG.columns;
784
- const RANK_KEY = CONFIG.rankKey;
785
- const colByKey = new Map(COLUMNS.map(c => [c.key, c]));
786
- const VARIANTS = CONFIG.variants.filter(v => POINTS.some(p => p.variant === v));
787
-
788
- // How a variant is written out. The short "Tuned + Ens." form is the internal key
789
- // (the charts' colour and marker tables are keyed by it), but nothing on the page
790
- // should show the abbreviation: the row tag uses the spelling the data and the
791
- // app's cross-subset table use, and the toggle spells it out in full.
792
- const VARIANT_TEXT = {
793
- "Default": "default",
794
- "Tuned": "tuned",
795
- "Tuned + Ens.": "tuned + ensembled",
796
- };
797
- const VARIANT_BTN = { "Tuned + Ens.": "Tuned + Ensembled" };
798
- const variantText = v => VARIANT_TEXT[v] || v;
799
- const HAS_IMPUTED = POINTS.some(p => p.imputed);
800
-
801
- const state = {
802
- methods: new Set(POINTS.map(p => p.method)),
803
- variants: new Set(VARIANTS),
804
- columns: new Set(COLUMNS.filter(c => c.on !== false).map(c => c.key)),
805
- imputed: true,
806
- search: "",
807
- sortKey: null,
808
- sortAsc: false,
809
- };
810
-
811
- const tblwrap = document.getElementById("tblwrap");
812
- const chipsBox = document.getElementById("chips");
813
- const famChips = new Map();
814
- const chipByMethod = new Map();
815
-
816
- // ---------- heat shading ----------
817
- // Green (best) through olive to red (worst), matching the website's
818
- // cross-subset overview so a reader moving between them reads one scale.
819
- const RAMP = [[0, [28, 120, 62]], [0.5, [138, 122, 36]], [1, [160, 58, 58]]];
820
- function heatColor(frac) {
821
- const f = Math.max(0, Math.min(1, frac));
822
- for (let i = 0; i < RAMP.length - 1; i++) {
823
- const [f0, c0] = RAMP[i], [f1, c1] = RAMP[i + 1];
824
- if (f <= f1) {
825
- const t = f1 === f0 ? 0 : (f - f0) / (f1 - f0);
826
- const mix = c0.map((v, j) => Math.round(v + t * (c1[j] - v)));
827
- return "rgb(" + mix.join(",") + ")";
828
- }
829
- }
830
- return "rgb(" + RAMP[RAMP.length - 1][1].join(",") + ")";
831
- }
832
- // Runtimes span orders of magnitude; shading them linearly paints every model
833
- // the same green and only the slowest one red, so those normalize in log space.
834
- function scaleOf(col, v) { return col.logScale ? Math.log10(v) : v; }
835
- function bounds(rows, col) {
836
- const values = [];
837
- for (const p of rows) {
838
- const v = p[col.key];
839
- if (v == null || !isFinite(v)) continue;
840
- if (col.logScale && !(v > 0)) continue;
841
- values.push(scaleOf(col, v));
842
- }
843
- if (values.length < 2) return null;
844
- const lo = Math.min(...values), hi = Math.max(...values);
845
- return hi > lo ? [lo, hi] : null;
846
- }
847
-
848
- // ---------- cells ----------
849
- function nameCell(p) {
850
- const ink = FAM_INK[p.family] || "var(--muted)";
851
- let inner = escapeHtml(p.method);
852
- if (p.variant) inner += ' <span class="variant-tag">(' + escapeHtml(variantText(p.variant)) + ")</span>";
853
- if (p.verified) inner += ' <span class="verified" title="Verified implementation">&#10004;&#65039;</span>';
854
- if (p.imputed) {
855
- const pct = isFinite(p.imputed_pct) ? fmtNum(p.imputed_pct, 0) + "% " : "";
856
- inner += ' <span class="imp-mark" title="' + pct + 'imputed">&#8225;</span>';
857
- }
858
- const body = p.url
859
- ? '<a href="' + escapeHtml(p.url) + '" target="_blank" rel="noopener" style="color:' + ink +
860
- ';font-weight:600;">' + inner + '<span class="link-icon">&#8599;</span></a>'
861
- : '<span style="color:' + ink + ';font-weight:600;">' + inner + "</span>";
862
- return '<td class="name" data-export="' + escapeHtml(plainName(p)) + '">' + body + "</td>";
863
- }
864
- function plainName(p) {
865
- return p.method + (p.variant ? " (" + p.variant + ")" : "");
866
- }
867
- function escapeHtml(text) {
868
- return String(text == null ? "" : text).replace(/[&<>"']/g, c =>
869
- ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
870
- }
871
-
872
- // ---------- rows ----------
873
- function visibleRows() {
874
- const term = state.search.trim().toLowerCase();
875
- return POINTS.filter(p => {
876
- if (!state.methods.has(p.method)) return false;
877
- if (p.variant && !state.variants.has(p.variant)) return false;
878
- if (!state.imputed && p.imputed) return false;
879
- if (term && !(p.method + " " + (p.variant || "") + " " + p.family).toLowerCase().includes(term)) return false;
880
- return true;
881
- });
882
- }
883
- function sortRows(rows) {
884
- const out = rows.slice();
885
- if (!state.sortKey) return out.sort((a, b) => a.position - b.position);
886
- const col = colByKey.get(state.sortKey);
887
- const sign = state.sortAsc ? 1 : -1;
888
- const keyOf = p => {
889
- const v = p[state.sortKey];
890
- if (col && col.text) return String(v == null ? "" : v).toLowerCase();
891
- return v == null || !isFinite(v) ? null : v;
892
- };
893
- return out.sort((a, b) => {
894
- const ka = keyOf(a), kb = keyOf(b);
895
- if (ka === null || kb === null) return ka === kb ? 0 : ka === null ? 1 : -1;
896
- return ka < kb ? -sign : ka > kb ? sign : 0;
897
- });
898
- }
899
-
900
- function activeColumns() {
901
- return COLUMNS.filter(c => c.always || state.columns.has(c.key));
902
- }
903
-
904
- function render() {
905
- const rows = sortRows(visibleRows());
906
- const cols = activeColumns();
907
- if (!rows.length) {
908
- tblwrap.innerHTML = '<p class="lbt-empty">Nothing selected — turn a family or variant back on.</p>';
909
- document.getElementById("caption").textContent = "";
910
- postHeight();
911
- return;
912
- }
913
- // No medals here: this table is the full ranking, so the position column
914
- // already says who is where. They belong to the cross-subset overview.
915
- const heat = new Map();
916
- for (const col of cols) if (col.heatmap) heat.set(col.key, bounds(rows, col));
917
-
918
- let html = '<table class="lbt"><thead><tr>';
919
- for (const col of cols) {
920
- const sorted = state.sortKey === col.key
921
- ? ' aria-sort="' + (state.sortAsc ? "ascending" : "descending") + '"' : "";
922
- html += "<th" + sorted + ' data-key="' + col.key + '" title="' + escapeHtml(col.hint || col.label) +
923
- '"><span class="lbl">' + escapeHtml(col.label) + "</span>" +
924
- (col.key === "elo" ? ' <span class="ci">(95% CI)</span>' : "") + "</th>";
925
- }
926
- html += "</tr></thead><tbody>";
927
- for (const p of rows) {
928
- html += "<tr>";
929
- for (const col of cols) {
930
- if (col.key === "position") {
931
- html += '<td class="pos num" data-sort="' + p.position + '">' + p.position + "</td>";
932
- } else if (col.key === "family") {
933
- const mark = FAM_VAR[p.family] || "var(--fam-baseline)";
934
- html += '<td class="type" data-sort="' + escapeHtml(p.family) + '" data-export="' + escapeHtml(p.family) +
935
- '"><span class="pill" style="background:color-mix(in srgb, ' + mark +
936
- ' 13%, transparent);color:' + (FAM_INK[p.family] || "var(--muted)") +
937
- ";border:1px solid color-mix(in srgb, " + mark + ' 40%, transparent);">' +
938
- escapeHtml(p.family_symbol || "") + "</span></td>";
939
- } else if (col.key === "model") {
940
- html += nameCell(p);
941
- } else {
942
- html += valueCell(p, col, heat.get(col.key));
943
- }
944
- }
945
- html += "</tr>";
946
- }
947
- tblwrap.innerHTML = html + "</tbody></table>";
948
- for (const th of tblwrap.querySelectorAll("th")) {
949
- th.addEventListener("click", () => sortBy(th.dataset.key));
950
- }
951
- document.getElementById("caption").textContent =
952
- rows.length + (rows.length === 1 ? " row" : " rows") +
953
- " · click a column header to sort, hover one for what it means" +
954
- (heat.size ? " · green is better, red is worse, per column" : "") +
955
- (HAS_IMPUTED ? " · ‡ marks a partly imputed score" : "");
956
- postHeight();
957
- }
958
-
959
- function valueCell(p, col, span) {
960
- const v = p[col.key];
961
- if (v == null || (typeof v === "number" && !isFinite(v))) return '<td class="na">&ndash;</td>';
962
- if (col.text) {
963
- return '<td data-sort="' + escapeHtml(v) + '" data-export="' + escapeHtml(v) + '">' + escapeHtml(v) + "</td>";
964
- }
965
- let style = "";
966
- if (span) {
967
- let frac = (scaleOf(col, v) - span[0]) / (span[1] - span[0]);
968
- if (!col.lowerBetter) frac = 1 - frac;
969
- style = ' style="background:' + heatColor(frac) + ';color:#f7f7f7;"';
970
- }
971
- let text = fmtNum(v, col.decimals) + (col.suffix || "");
972
- let ci = "";
973
- if (col.key === "elo" && p.elo_ci) {
974
- ci = ' <span class="ci">(' + escapeHtml(p.elo_ci) + ")</span>";
975
- }
976
- return '<td class="num" data-sort="' + v + '"' + (ci ? ' data-ci="' + escapeHtml(p.elo_ci) + '"' : "") +
977
- style + ">" + text + ci + "</td>";
978
- }
979
-
980
- function sortBy(key) {
981
- if (!key) return;
982
- if (state.sortKey === key) state.sortAsc = !state.sortAsc;
983
- else { state.sortKey = key; state.sortAsc = !!(colByKey.get(key) || {}).lowerBetter; }
984
- render();
985
- }
986
-
987
- // ---------- chips (families and their methods) ----------
988
- function familyMembers(fam) {
989
- return [...new Set(POINTS.filter(p => p.family === fam).map(p => p.method))];
990
- }
991
- function methodRank(name) {
992
- const col = colByKey.get(RANK_KEY);
993
- const values = POINTS.filter(p => p.method === name && isFinite(p[RANK_KEY])).map(p => p[RANK_KEY]);
994
- if (!values.length) return Infinity;
995
- return col.lowerBetter ? Math.min(...values) : -Math.max(...values);
996
- }
997
- function buildChips() {
998
- const head = document.createElement("div");
999
- head.className = "chips-head";
1000
- head.textContent = "Models shown — click to remove, click a family to toggle the whole group";
1001
- chipsBox.appendChild(head);
1002
- for (const fam of FAM_ORDER) {
1003
- const members = familyMembers(fam);
1004
- if (!members.length) continue;
1005
- members.sort((a, b) => methodRank(a) - methodRank(b));
1006
- const row = document.createElement("div");
1007
- row.className = "chiprow";
1008
- const famBtn = document.createElement("button");
1009
- famBtn.className = "famchip";
1010
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1011
- famBtn.innerHTML = famChipLabel(fam, members.length);
1012
- famBtn.title = "Toggle all " + members.length + " " + fam + " models";
1013
- famBtn.addEventListener("click", () => toggleFamily(fam));
1014
- row.appendChild(famBtn);
1015
- famChips.set(fam, famBtn);
1016
- const set = document.createElement("div");
1017
- set.className = "chipset";
1018
- for (const name of members) {
1019
- const imputed = POINTS.some(p => p.method === name && p.imputed);
1020
- const b = document.createElement("button");
1021
- b.className = "chip";
1022
- b.style.setProperty("--fam", FAM_VAR[fam]);
1023
- b.innerHTML = '<span class="dot"></span><span>' + escapeHtml(name) + "</span>" +
1024
- (imputed ? '<span class="imp-mark">&#8225;</span>' : "");
1025
- b.title = name + (imputed ? " — partially imputed" : "");
1026
- b.addEventListener("click", () => toggleMethod(name));
1027
- set.appendChild(b);
1028
- chipByMethod.set(name, b);
1029
- }
1030
- row.appendChild(set);
1031
- chipsBox.appendChild(row);
1032
- }
1033
- }
1034
- function syncChips() {
1035
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.methods.has(name)));
1036
- for (const [fam, b] of famChips) {
1037
- b.setAttribute("aria-pressed", String(familyMembers(fam).every(m => state.methods.has(m))));
1038
- }
1039
- }
1040
- function toggleMethod(name) {
1041
- if (state.methods.has(name)) state.methods.delete(name); else state.methods.add(name);
1042
- syncChips();
1043
- render();
1044
- }
1045
- function toggleFamily(fam) {
1046
- const members = familyMembers(fam);
1047
- const allOn = members.every(m => state.methods.has(m));
1048
- for (const m of members) { if (allOn) state.methods.delete(m); else state.methods.add(m); }
1049
- syncChips();
1050
- render();
1051
- }
1052
-
1053
- // ---------- variant / imputed / column toggles ----------
1054
- const variantBtns = new Map();
1055
- function buildVariantBtns() {
1056
- const box = document.getElementById("variant-btns");
1057
- for (const v of VARIANTS) {
1058
- const b = document.createElement("button");
1059
- b.className = "btn toggle";
1060
- b.innerHTML = '<span class="swatch"></span>' + escapeHtml(VARIANT_BTN[v] || v);
1061
- b.style.setProperty("--fam", VARIANT_VAR[v] || "var(--accent)");
1062
- b.title = "Show or hide the " + variantText(v) + " results";
1063
- b.addEventListener("click", () => {
1064
- if (state.variants.has(v)) state.variants.delete(v); else state.variants.add(v);
1065
- syncVariantBtns();
1066
- render();
1067
- });
1068
- box.appendChild(b);
1069
- variantBtns.set(v, b);
1070
- }
1071
- }
1072
- function syncVariantBtns() {
1073
- for (const [v, b] of variantBtns) b.setAttribute("aria-pressed", String(state.variants.has(v)));
1074
- const imp = document.getElementById("btn-imputed");
1075
- imp.hidden = !HAS_IMPUTED;
1076
- imp.className = "btn toggle";
1077
- imp.innerHTML = '<span class="swatch"></span>&#8225; Imputed';
1078
- imp.setAttribute("aria-pressed", String(state.imputed));
1079
- imp.style.setProperty("--fam", "var(--muted)");
1080
- }
1081
- function buildColumnBtns() {
1082
- const box = document.getElementById("col-btns");
1083
- for (const col of COLUMNS) {
1084
- if (col.always) continue;
1085
- const b = document.createElement("button");
1086
- b.className = "btn toggle";
1087
- b.textContent = col.short || col.label;
1088
- b.title = "Show or hide " + col.label;
1089
- b.style.setProperty("--fam", "var(--accent)");
1090
- b.addEventListener("click", () => {
1091
- if (state.columns.has(col.key)) state.columns.delete(col.key); else state.columns.add(col.key);
1092
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1093
- render();
1094
- });
1095
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1096
- box.appendChild(b);
1097
- }
1098
- }
1099
-
1100
- // ---------- CSV ----------
1101
- // Exports what the reader is looking at: the rows the filters left, the
1102
- // columns they picked, in the order they sorted.
1103
- function downloadCsv() {
1104
- const cols = activeColumns();
1105
- const rows = sortRows(visibleRows());
1106
- const quote = v => '"' + String(v == null ? "" : v).replace(/"/g, '""') + '"';
1107
- const head = [];
1108
- for (const col of cols) {
1109
- head.push(col.label);
1110
- if (col.key === "elo" && rows.some(p => p.elo_ci)) head.push("Elo 95% CI");
1111
- }
1112
- const lines = [head.map(quote).join(",")];
1113
- for (const p of rows) {
1114
- const values = [];
1115
- for (const col of cols) {
1116
- if (col.key === "model") values.push(plainName(p));
1117
- else values.push(p[col.key]);
1118
- if (col.key === "elo" && rows.some(q => q.elo_ci)) values.push(p.elo_ci || "");
1119
- }
1120
- lines.push(values.map(quote).join(","));
1121
- }
1122
- const blob = new Blob([lines.join("\n") + "\n"], { type: "text/csv;charset=utf-8" });
1123
- const url = URL.createObjectURL(blob);
1124
- downloadUrl(url, slugify(CONFIG.title || document.title || "leaderboard") + ".csv");
1125
- URL.revokeObjectURL(url);
1126
- }
1127
-
1128
- // ---------- boot ----------
1129
- if (CONFIG.title) document.getElementById("title").textContent = CONFIG.title;
1130
- document.getElementById("search").addEventListener("input", ev => {
1131
- state.search = ev.target.value;
1132
- render();
1133
- });
1134
- document.getElementById("btn-imputed").addEventListener("click", () => {
1135
- state.imputed = !state.imputed;
1136
- syncVariantBtns();
1137
- render();
1138
- });
1139
- document.getElementById("btn-csv").addEventListener("click", downloadCsv);
1140
- // Embedded, the host page puts its own CSV button in the panel header (next to
1141
- // the title) and asks for the download over postMessage, the same way it drives
1142
- // the figure exports. Standalone, this page needs its own button.
1143
- if (window.parent !== window) {
1144
- document.getElementById("btn-csv").hidden = true;
1145
- window.addEventListener("message", ev => {
1146
- if (ev.data && ev.data.type === "tabarena-leaderboard-csv") downloadCsv();
1147
- });
1148
- }
1149
- // Opens with the controls: the filters are the point of this page, not chrome
1150
- // around a figure.
1151
- setUpPaperView(render, { openInPaper: false });
1152
- buildVariantBtns();
1153
- buildColumnBtns();
1154
- buildChips();
1155
- syncChips();
1156
- syncVariantBtns();
1157
- render();
1158
- window.addEventListener("resize", postHeight);
1159
- })();
1160
- </script>
1161
- </body>
1162
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/n_datasets_51 DELETED
File without changes
data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_explorer.html DELETED
@@ -1,1230 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena Pareto explorer</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Two-column layout: controls + chips in a side panel, chart beside it.
269
- ``chips-right`` mirrors the columns. Wraps to stacked when narrow. */
270
- .explorer-grid { display: flex; gap: 18px; align-items: flex-start; }
271
- .explorer-grid.chips-right { flex-direction: row-reverse; }
272
- .sidebox { flex: 0 0 330px; min-width: 250px; display: flex; flex-direction: column; gap: 10px; }
273
- .mainbox { flex: 1 1 auto; min-width: 0; }
274
- @media (max-width: 860px) {
275
- .explorer-grid { flex-wrap: wrap; }
276
- .sidebox { flex: 1 1 100%; }
277
- }
278
-
279
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
280
- .chartbox { position: relative; }
281
- /* The chart is sized in device pixels by render() rather than scaled from a
282
- viewBox: scaling stretched the type along with the plot, and a viewBox tall
283
- enough to read made the whole panel own the screen. */
284
- .chartbox svg { display: block; }
285
- /* Chips scroll within the column so the panel height follows the chart. */
286
- .sidebox .chips { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent; }
287
- .sidebox .chips::-webkit-scrollbar { width: 9px; }
288
- .sidebox .chips::-webkit-scrollbar-thumb {
289
- background: var(--pt-muted); border-radius: 8px; border: 3px solid transparent; background-clip: content-box;
290
- }
291
- </style>
292
- </head>
293
- <body>
294
- <div class="viewbar">
295
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
296
- </div>
297
- <p class="explorer-title" id="title"></p>
298
- <div class="explorer-grid" id="grid">
299
- <div class="sidebox">
300
- <div class="controls">
301
- <label class="metricpick" id="metricpick" hidden>Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <div class="btnrow">
305
- <button class="btn" id="btn-front">Pareto front</button>
306
- <button class="btn" id="btn-all">All</button>
307
- <button class="btn" id="btn-none">Clear</button>
308
- </div>
309
- <span class="hint">Click methods or family buttons to highlight &middot; hover points for details</span>
310
- </div>
311
- <div class="chips" id="chips"></div>
312
- </div>
313
- <div class="mainbox">
314
- <div class="exportbar" id="exportbar" hidden>
315
- <span class="hint">Export figure</span>
316
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
317
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
318
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
319
- </div>
320
- <!-- Legend above the chart so readers decode the marks before the data. -->
321
- <div class="legendstrip" id="legendstrip"></div>
322
- <div class="chartbox" id="chartbox">
323
- <svg id="chart" role="img" aria-label="Pareto front explorer"></svg>
324
- <div class="tooltip"></div>
325
- </div>
326
- </div>
327
- </div>
328
- <details class="datatable">
329
- <summary>Data table</summary>
330
- <div class="tblwrap" id="tblwrap"></div>
331
- </details>
332
-
333
- <script>
334
- (function () {
335
- "use strict";
336
- const CONFIG = {"mode": "scatter", "title": null, "metrics": [{"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": ""}], "xAxes": [{"key": "x_infer", "label": "Inference time", "axisLabel": "Inference time per 1K samples (s), median \u2014 log scale", "short": "Inference (s/1K, median)"}], "chipsSide": "left"};
337
- const POINTS = [{"method":"CatBoost","family":"Tree-based","x_infer":0.0248961767,"imp":15.5244408181,"elo":1378.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"CatBoost","family":"Tree-based","x_infer":0.0380928654,"imp":14.8850630485,"elo":1408.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"CatBoost","family":"Tree-based","x_infer":0.3442282032,"imp":14.655679873,"elo":1421.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0325848325,"imp":18.6570877535,"elo":1263.9,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0336512776,"imp":17.5510939543,"elo":1308.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.4040760969,"imp":17.2125514645,"elo":1333.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"EBM","family":"Tree-based","x_infer":0.0143528648,"imp":20.2553633192,"elo":1203.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"EBM","family":"Tree-based","x_infer":0.0150308987,"imp":19.3136682806,"elo":1235.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"EBM","family":"Tree-based","x_infer":0.1412900124,"imp":18.6722157117,"elo":1271.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.2834081321,"imp":26.1764963285,"elo":1009.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.0712129939,"imp":20.5638085151,"elo":1180.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.7662402325,"imp":19.7773891991,"elo":1208.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.3731781389,"imp":25.6818545229,"elo":1007.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.3063915412,"imp":21.0963185218,"elo":1146.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"FastaiMLP","family":"Neural Network","x_infer":4.4666950703,"imp":19.6768711642,"elo":1207.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LightGBM","family":"Tree-based","x_infer":0.1416787942,"imp":19.3442915921,"elo":1186.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"LightGBM","family":"Tree-based","x_infer":0.3337603531,"imp":15.6876116943,"elo":1385.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"LightGBM","family":"Tree-based","x_infer":2.638673831,"imp":15.0334967342,"elo":1432.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LimiX","family":"Foundation Model","x_infer":6.2423742585,"imp":15.5263025459,"elo":1362.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.3073466784,"imp":19.1942294206,"elo":1248.1,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.4747242315,"imp":16.0439590519,"elo":1355.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ModernNCA","family":"Neural Network","x_infer":8.1379168675,"imp":15.5985458077,"elo":1389.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.0285805066,"imp":31.7480551938,"elo":936.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.2581384468,"imp":27.801643493,"elo":1050.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.5997539679,"imp":26.4456425257,"elo":1088.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RandomForest","family":"Tree-based","x_infer":0.0535405758,"imp":26.2650042304,"elo":1000.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RandomForest","family":"Tree-based","x_infer":0.0852279186,"imp":21.5350457807,"elo":1137.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RandomForest","family":"Tree-based","x_infer":0.7709478321,"imp":20.8284764332,"elo":1170.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealMLP","family":"Neural Network","x_infer":1.6887954948,"imp":18.5692266729,"elo":1229.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealMLP","family":"Neural Network","x_infer":0.663132055,"imp":14.2021817976,"elo":1426.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealMLP","family":"Neural Network","x_infer":11.9749853765,"imp":12.8814924929,"elo":1507.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":0.6449695981,"imp":11.6604512027,"elo":1524.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":1.2178786994,"imp":11.1161483399,"elo":1554.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":8.9076602204,"imp":10.4265657302,"elo":1594.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"SAP-RPT-OSS","family":"Foundation Model","x_infer":2.0745579697,"imp":18.2783205087,"elo":1283.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":43.7378122226,"imp":17.2252827702,"elo":1324.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":39.9597364603,"imp":14.9360532408,"elo":1402.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabDPT","family":"Foundation Model","x_infer":286.5373788357,"imp":13.7879250832,"elo":1457.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabDPT-Turbo","family":"Foundation Model","x_infer":0.1824194029,"imp":13.9998432493,"elo":1459.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabFM","family":"Foundation Model","x_infer":6.9854290718,"imp":4.0744823648,"elo":1791.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabICLv2","family":"Foundation Model","x_infer":0.1505492146,"imp":10.1751538315,"elo":1593.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.2435252328,"imp":17.0001433318,"elo":1297.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.2512939462,"imp":14.951713351,"elo":1393.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabM","family":"Neural Network","x_infer":2.2468658586,"imp":14.0806210836,"elo":1450.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabPFN-2.6","family":"Foundation Model","x_infer":0.5548034626,"imp":10.7505455244,"elo":1619.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabPFN-3","family":"Foundation Model","x_infer":0.3988194969,"imp":9.3282966338,"elo":1665.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":5.3480868421,"imp":30.4556099407,"elo":993.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":4.2680534632,"imp":25.584307557,"elo":1097.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabSTAR","family":"Foundation Model","x_infer":18.8114333153,"imp":25.6000754281,"elo":1096.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabSwift","family":"Foundation Model","x_infer":0.0719683769,"imp":16.0807432886,"elo":1350.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.1258046788,"imp":22.8151308708,"elo":1074.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.1311128328,"imp":18.876878914,"elo":1211.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TorchMLP","family":"Neural Network","x_infer":1.95162543,"imp":17.1611523587,"elo":1285.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"XGBoost","family":"Tree-based","x_infer":0.1226232881,"imp":18.7922233221,"elo":1214.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"XGBoost","family":"Tree-based","x_infer":0.3080760155,"imp":16.0650443256,"elo":1350.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"XGBoost","family":"Tree-based","x_infer":1.689445163,"imp":15.7538195428,"elo":1375.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"iLTM","family":"Foundation Model","x_infer":68.173296937,"imp":23.0236433289,"elo":1094.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"iLTM","family":"Foundation Model","x_infer":62.1299021172,"imp":17.1472473791,"elo":1295.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"iLTM","family":"Foundation Model","x_infer":464.3696034625,"imp":15.3035551324,"elo":1404.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"xRFM","family":"Other","x_infer":0.9186215876,"imp":26.4448633944,"elo":1043.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"xRFM","family":"Other","x_infer":0.1302743502,"imp":17.4492208687,"elo":1300.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"xRFM","family":"Other","x_infer":2.5492595349,"imp":16.2842179754,"elo":1347.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."}];
338
-
339
-
340
- const NS = "http://www.w3.org/2000/svg";
341
- // Baseline and Other are one bucket, as in the site's own type legend: they
342
- // already share a color, and each holds only a handful of methods.
343
- const FAM_MERGED = "Baseline / Other";
344
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
345
- // Normalized up front so every later lookup — colors, chips, sorting — sees
346
- // the merged family. Both templates declare POINTS above this block.
347
- for (const p of POINTS) p.family = famOf(p.family);
348
-
349
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
350
- const FAM_VAR = {
351
- "Foundation Model": "var(--fam-foundation)",
352
- "Tree-based": "var(--fam-tree)",
353
- "Neural Network": "var(--fam-nn)",
354
- "Reference Pipeline": "var(--fam-reference)",
355
- [FAM_MERGED]: "var(--fam-baseline)",
356
- };
357
- // The symbol the website shows for each family, so a family chip here reads the
358
- // same as the Type column on the site. Baseline and Other are one bucket, so
359
- // that chip carries both symbols.
360
- const FAM_SYMBOL = {
361
- "Foundation Model": "🧠⚡",
362
- "Tree-based": "🌳",
363
- "Neural Network": "🧠🔁",
364
- "Reference Pipeline": "📊",
365
- [FAM_MERGED]: "📏 ❓",
366
- };
367
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
368
- const VARIANT_VAR = {
369
- "Default": "var(--var-default)",
370
- "Tuned": "var(--var-tuned)",
371
- "Tuned + Ens.": "var(--var-tunedens)",
372
- };
373
-
374
- // A family chip's label: its symbol, its name and how many methods it holds.
375
- function famChipLabel(family, count) {
376
- const symbol = FAM_SYMBOL[family];
377
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
378
- family + ' <span class="count">&times;' + count + "</span>";
379
- }
380
-
381
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
382
- const FAM_INK = {
383
- "Foundation Model": "var(--fam-foundation-ink)",
384
- "Tree-based": "var(--fam-tree-ink)",
385
- "Neural Network": "var(--fam-nn-ink)",
386
- "Reference Pipeline": "var(--fam-reference-ink)",
387
- [FAM_MERGED]: "var(--fam-baseline-ink)",
388
- };
389
-
390
- // Create an SVG element with attributes, optionally appended to `parent`.
391
- function el(name, attrs, parent) {
392
- const node = document.createElementNS(NS, name);
393
- for (const k in attrs) node.setAttribute(k, attrs[k]);
394
- if (parent) parent.appendChild(node);
395
- return node;
396
- }
397
-
398
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
399
- // locale-independent by definition, which is the point: `toLocaleString`
400
- // would follow the *viewer's* browser locale and print 1234,5 for a German
401
- // visitor, disagreeing with the figures and CSVs beside it.
402
- function fmtNum(v, decimals) {
403
- if (v == null || !isFinite(v)) return "—";
404
- return v.toFixed(decimals);
405
- }
406
-
407
- function fmtMetric(metric, v) {
408
- if (v == null || !isFinite(v)) return "—";
409
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
410
- }
411
-
412
- function fmtTime(v) {
413
- if (v >= 100) return fmtNum(v, 0) + " s";
414
- if (v >= 1) return fmtNum(v, 1) + " s";
415
- if (v >= 0.1) return fmtNum(v, 2) + " s";
416
- return fmtNum(v, 3) + " s";
417
- }
418
-
419
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
420
- function niceStep(raw) {
421
- if (!(raw > 0)) return 1;
422
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
423
- for (const m of [1, 2, 2.5, 5, 10]) {
424
- if (mag * m >= raw) return mag * m;
425
- }
426
- return mag * 10;
427
- }
428
-
429
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
430
- // index rather than by accumulation so fractional steps do not drift.
431
- function ticks(min, max, target) {
432
- const step = niceStep((max - min) / target);
433
- const first = Math.ceil(min / step);
434
- const out = [];
435
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
436
- return out.length ? out : [min];
437
- }
438
-
439
- // A hover tooltip anchored inside `box` (which must be position:relative).
440
- function makeTooltip(box) {
441
- const node = box.querySelector(".tooltip");
442
- return {
443
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
444
- move(ev) {
445
- const r = box.getBoundingClientRect();
446
- let tx = ev.clientX - r.left + 14;
447
- const ty = ev.clientY - r.top + 12;
448
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
449
- node.style.left = tx + "px";
450
- node.style.top = ty + "px";
451
- },
452
- hide() { node.style.display = "none"; },
453
- };
454
- }
455
-
456
- // Paper view — white surface, legend + chart only — is the *default*: what a
457
- // reader wants first is the figure, and it is the state worth exporting. The
458
- // controls, chip list and data table are one click away behind "Edit view".
459
- // `afterToggle` re-renders charts whose size is measured from the layout.
460
- // `options.openInPaper` (default true) decides the state the page opens in. A
461
- // chart opens as the figure; the leaderboard table opens with its controls,
462
- // since there the interaction is the point rather than scaffolding around it.
463
- function setUpPaperView(afterToggle, options) {
464
- const opts = options || {};
465
- const root = document.documentElement;
466
- let hostTheme = null; // the embedding page's choice, captured on entry
467
- const btn = document.getElementById("btn-paper");
468
- const embedded = window.parent !== window;
469
-
470
- function setPaper(on) {
471
- document.body.classList.toggle("paper", on);
472
- if (on) {
473
- hostTheme = root.getAttribute("data-theme");
474
- root.setAttribute("data-theme", "light");
475
- } else if (hostTheme) {
476
- root.setAttribute("data-theme", hostTheme);
477
- } else {
478
- root.removeAttribute("data-theme");
479
- }
480
- btn.textContent = on ? "Edit view" : "Paper view";
481
- document.getElementById("exportbar").hidden = !on || embedded;
482
- if (afterToggle) requestAnimationFrame(afterToggle);
483
- postHeight();
484
- }
485
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
486
- // Embedded, the host page owns these controls — they sit beside the panel's
487
- // static-figure toggle and are driven from the outside. Standalone (the
488
- // shareable single file) this page needs its own.
489
- if (embedded) document.querySelector(".viewbar").hidden = true;
490
- window.addEventListener("message", ev => {
491
- const d = ev.data;
492
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
493
- });
494
- // Only standalone: embedded, the host owns the button and would not see the
495
- // key press, so its label would fall out of step with the frame.
496
- if (!embedded) {
497
- document.addEventListener("keydown", ev => {
498
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
499
- });
500
- }
501
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
502
- }
503
-
504
- // --- Figure export ---------------------------------------------------------
505
- // The chart is live SVG, so a file can be built from it directly. Three things
506
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
507
- // nothing outside this document), it has no background or font of its own, and
508
- // the legend is HTML rather than part of the SVG.
509
-
510
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
511
- // cloned and its label re-emitted at the measured position. foreignObject would
512
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
513
- // break the PNG path.
514
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
515
- // once the node leaves this document.
516
- function resolveVars(root, resolve) {
517
- for (const node of [root, ...root.querySelectorAll("*")]) {
518
- for (const attr of ["fill", "stroke"]) {
519
- const value = node.getAttribute(attr);
520
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
521
- }
522
- }
523
- }
524
-
525
- function legendToSvg(container, resolve) {
526
- const base = container.getBoundingClientRect();
527
- const group = document.createElementNS(NS, "g");
528
- let height = 0;
529
- for (const item of container.querySelectorAll(".item")) {
530
- const box = item.getBoundingClientRect();
531
- if (!box.width) continue;
532
- height = Math.max(height, box.bottom - base.top);
533
- let textLeft = box.left - base.left;
534
- const glyph = item.querySelector("svg");
535
- if (glyph) {
536
- const gbox = glyph.getBoundingClientRect();
537
- const wrap = el("g", {
538
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
539
- }, group);
540
- const glyphClone = glyph.cloneNode(true);
541
- resolveVars(glyphClone, resolve);
542
- wrap.appendChild(glyphClone);
543
- textLeft = gbox.right - base.left + 5;
544
- }
545
- const label = item.textContent.trim();
546
- if (!label) continue;
547
- const colored = item.querySelector("[style*='color']");
548
- const text = el("text", {
549
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
550
- fill: resolve(getComputedStyle(colored || item).color),
551
- }, group);
552
- text.textContent = label;
553
- }
554
- return { group, height: Math.ceil(height) };
555
- }
556
-
557
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
558
- // y-axis beside the scrolling plot) still exports as one figure.
559
- function buildExportSvg(parts, legendEl, pad = 10) {
560
- const rootStyle = getComputedStyle(document.documentElement);
561
- const resolve = value => String(value).replace(
562
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
563
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
564
-
565
- let chartW = 0, chartH = 0;
566
- for (const part of parts) {
567
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
568
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
569
- }
570
-
571
- const out = document.createElementNS(NS, "svg");
572
- out.setAttribute("xmlns", NS);
573
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
574
- let top = pad;
575
- const later = []; // built after the width is known
576
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
577
- if (legend && legend.height) {
578
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
579
- later.push(() => out.appendChild(legend.group));
580
- top += legend.height + 8;
581
- }
582
-
583
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
584
- const height = top + chartH + pad;
585
- out.setAttribute("width", Math.ceil(width));
586
- out.setAttribute("height", Math.ceil(height));
587
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
588
- for (const build of later) build();
589
-
590
- for (const part of parts) {
591
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
592
- const clone = part.svg.cloneNode(true);
593
- resolveVars(clone, resolve);
594
- while (clone.firstChild) group.appendChild(clone.firstChild);
595
- }
596
- return out;
597
- }
598
-
599
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
600
- function slugify(text) {
601
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
602
- }
603
-
604
- function downloadUrl(url, filename) {
605
- const a = document.createElement("a");
606
- a.href = url;
607
- a.download = filename;
608
- document.body.appendChild(a);
609
- a.click();
610
- a.remove();
611
- }
612
-
613
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
614
- function rasterize(svg, scale, done, fail) {
615
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
616
- const img = new Image();
617
- img.onload = () => {
618
- const canvas = document.createElement("canvas");
619
- canvas.width = Math.round(width * scale);
620
- canvas.height = Math.round(height * scale);
621
- const ctx = canvas.getContext("2d");
622
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
623
- ctx.drawImage(img, 0, 0);
624
- done(canvas, width, height);
625
- };
626
- img.onerror = fail;
627
- img.src = "data:image/svg+xml;charset=utf-8,"
628
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
629
- }
630
-
631
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
632
- // cost this page its zero-dependency, single-file property. The image is stored
633
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
634
- // in points to the figure's CSS size, so it prints at the size it appears here
635
- // and the pixels land at 96*scale dpi.
636
- async function buildPdf(canvas, cssWidth, cssHeight) {
637
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
638
- const rgb = new Uint8Array((pixels.length / 4) * 3);
639
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
640
- rgb[j] = pixels[i];
641
- rgb[j + 1] = pixels[i + 1];
642
- rgb[j + 2] = pixels[i + 2];
643
- }
644
- const deflated = new Uint8Array(await new Response(
645
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
646
-
647
- const encoder = new TextEncoder();
648
- const chunks = [];
649
- const offsets = [];
650
- let cursor = 0;
651
- const put = data => {
652
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
653
- chunks.push(bytes);
654
- cursor += bytes.length;
655
- };
656
- const object = (id, body, stream) => {
657
- offsets[id] = cursor;
658
- put(`${id} 0 obj\n${body}\n`);
659
- if (stream) {
660
- put("stream\n");
661
- put(stream);
662
- put("\nendstream\n");
663
- }
664
- put("endobj\n");
665
- };
666
-
667
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
668
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
669
- put("%PDF-1.4\n");
670
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
671
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
672
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
673
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
674
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
675
- object(4, "<< /Type /XObject /Subtype /Image "
676
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
677
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
678
- object(5, `<< /Length ${content.length} >>`, content);
679
-
680
- const xref = cursor;
681
- let table = "xref\n0 6\n0000000000 65535 f \n";
682
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
683
- put(table);
684
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
685
- return new Blob(chunks, { type: "application/pdf" });
686
- }
687
-
688
- // Wire up the export buttons; `getParts` is called per click so the file always
689
- // matches what is on screen. Returns a `run(format)` the host page can drive.
690
- function setUpExport(getParts, basename) {
691
- const buttons = {
692
- svg: document.getElementById("btn-svg"),
693
- png: document.getElementById("btn-png"),
694
- pdf: document.getElementById("btn-pdf"),
695
- };
696
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
697
-
698
- // A sandboxed frame has no modals, so a failure is reported on the button.
699
- function complain(format) {
700
- const button = buttons[format];
701
- if (!button) return;
702
- const label = button.textContent;
703
- button.textContent = "failed";
704
- setTimeout(() => { button.textContent = label; }, 2500);
705
- }
706
-
707
- function run(format) {
708
- const svg = figure();
709
- const name = basename();
710
- if (format === "svg") {
711
- downloadUrl("data:image/svg+xml;charset=utf-8,"
712
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
713
- return;
714
- }
715
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
716
- // points so the pixels already land near 200 dpi at print size.
717
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
718
- if (format === "png") {
719
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
720
- } else {
721
- buildPdf(canvas, cssWidth, cssHeight)
722
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
723
- .catch(() => complain("pdf"));
724
- }
725
- }, () => complain(format));
726
- }
727
-
728
- for (const format of Object.keys(buttons)) {
729
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
730
- }
731
- // Embedded, the buttons live in the host's panel header (see main.taExport).
732
- window.addEventListener("message", ev => {
733
- const d = ev.data;
734
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
735
- });
736
- }
737
-
738
- // When embedded, report the content height so the host page can size the
739
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
740
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
741
- // clamped to at least the iframe's current viewport, which turns the
742
- // resize round-trip into a grow-forever feedback loop. The change guard
743
- // stops re-posting once the height settles.
744
- let lastPostedHeight = 0;
745
- function postHeight() {
746
- if (window.parent === window) return;
747
- const height = Math.ceil(document.body.offsetHeight);
748
- if (Math.abs(height - lastPostedHeight) < 3) return;
749
- lastPostedHeight = height;
750
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
751
- }
752
-
753
-
754
- const TRAJECTORY = CONFIG.mode === "trajectory";
755
-
756
- const titleEl = document.getElementById("title");
757
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
758
-
759
- // Column order: chips/controls left of the chart, or mirrored.
760
- document.getElementById("grid").classList.add(CONFIG.chipsSide === "right" ? "chips-right" : "chips-left");
761
-
762
- const svg = document.getElementById("chart");
763
- const box = document.getElementById("chartbox");
764
- const chipsBox = document.getElementById("chips");
765
- const controlsBox = document.querySelector(".controls");
766
- const tip = makeTooltip(box);
767
-
768
- // Marker glyph per variant at (cx, cy); trajectories use circles everywhere.
769
- function drawMark(parent, cx, cy, variant, color, size, opacity, dataM, whiteStroke) {
770
- const common = { opacity: opacity, "data-m": dataM };
771
- let node;
772
- if (TRAJECTORY || variant === "Default" || !variant) {
773
- node = el("circle", { ...common, cx, cy, r: size, fill: color }, parent);
774
- } else if (variant === "Tuned") {
775
- const s = size * 1.75;
776
- node = el("rect", { ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1.5, fill: color }, parent);
777
- } else if (variant === "Tuned + Ens.") {
778
- const d = size * 0.95;
779
- node = el("path", {
780
- ...common,
781
- d: `M${cx - d},${cy - d} L${cx + d},${cy + d} M${cx - d},${cy + d} L${cx + d},${cy - d}`,
782
- stroke: color, "stroke-width": size * 0.62, fill: "none", "stroke-linecap": "round",
783
- }, parent);
784
- } else {
785
- // Any other variant (e.g. "Baseline", holdout types): diamond.
786
- const s = size * 1.45;
787
- node = el("rect", {
788
- ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1,
789
- fill: color, transform: `rotate(45 ${cx} ${cy})`,
790
- }, parent);
791
- }
792
- if (whiteStroke && variant !== "Tuned + Ens.") {
793
- node.setAttribute("stroke", "var(--card)");
794
- node.setAttribute("stroke-width", "1");
795
- }
796
- return node;
797
- }
798
-
799
- function drawImputedRing(parent, cx, cy, size, color, opacity, dataM) {
800
- el("circle", {
801
- cx, cy, r: size + 4.5, fill: "none", stroke: color, "stroke-width": 1.4,
802
- "stroke-dasharray": "3 2.5", opacity: opacity, "data-m": dataM,
803
- }, parent);
804
- }
805
-
806
- // Fat green arrow pointing into the optimal corner (mirrors the static
807
- // figures' "Optimal" arrow so both read the same way).
808
- function drawOptimalArrow(parent, lowerBetter, M, W, H) {
809
- const cornerY = lowerBetter ? H - M.b - 12 : M.t + 12;
810
- const tailY = lowerBetter ? H - M.b - 64 : M.t + 64;
811
- const cx = M.l + 12, tx = M.l + 64;
812
- const dx = cx - tx, dy = cornerY - tailY;
813
- const len = Math.hypot(dx, dy);
814
- const ux = dx / len, uy = dy / len;
815
- const headLen = 16;
816
- const bx = cx - ux * headLen, by = cornerY - uy * headLen; // head base center
817
- // line stops at the head base
818
- el("line", {
819
- x1: tx, y1: tailY, x2: bx, y2: by,
820
- stroke: "var(--optimal)", "stroke-width": 13, "stroke-linecap": "round", opacity: 0.92,
821
- }, parent);
822
- const px = -uy, py = ux; // perpendicular
823
- el("polygon", {
824
- points: `${cx},${cornerY} ${bx + px * 11},${by + py * 11} ${bx - px * 11},${by - py * 11}`,
825
- fill: "var(--optimal)", opacity: 0.92,
826
- }, parent);
827
- let angle = Math.atan2(dy, dx) * 180 / Math.PI;
828
- if (angle > 90 || angle < -90) angle += 180;
829
- const mx = (tx + bx) / 2, my = (tailY + by) / 2;
830
- const t = el("text", {
831
- x: mx, y: my, "text-anchor": "middle", "dominant-baseline": "middle",
832
- "font-size": 10.5, "font-weight": 700, fill: "#ffffff",
833
- transform: `rotate(${angle} ${mx} ${my})`,
834
- }, parent);
835
- t.textContent = "Optimal";
836
- }
837
-
838
- // ---------- data ----------
839
- const byMethod = new Map();
840
- for (const p of POINTS) {
841
- if (!byMethod.has(p.method)) byMethod.set(p.method, []);
842
- byMethod.get(p.method).push(p); // insertion order = builder's point order
843
- }
844
-
845
- const METRICS = CONFIG.metrics;
846
- let metricKey = METRICS[0].key;
847
- const metricByKey = {};
848
- for (const m of METRICS) metricByKey[m.key] = m;
849
-
850
- // Single time axis per explorer (the scatter ships inference time, the
851
- // trajectories train time).
852
- const X_AXIS = CONFIG.xAxes[0];
853
- const xKey = X_AXIS.key;
854
-
855
- function mval(p, metric) { return p[metric.key]; }
856
-
857
- function computeFront(metric) {
858
- const xk = xKey;
859
- const pts = [...POINTS].sort((a, b) =>
860
- a[xk] - b[xk] || (metric.lowerBetter ? mval(a, metric) - mval(b, metric) : mval(b, metric) - mval(a, metric)));
861
- const verts = [];
862
- const methods = new Set();
863
- let best = null;
864
- for (const p of pts) {
865
- const v = mval(p, metric);
866
- if (best === null || (metric.lowerBetter ? v < best : v > best)) {
867
- if (best !== null) verts.push([p[xk], best]);
868
- verts.push([p[xk], v]);
869
- best = v;
870
- methods.add(p.method);
871
- }
872
- }
873
- return { verts, methods };
874
- }
875
-
876
- const state = { active: new Set(computeFront(metricByKey[metricKey]).methods) };
877
-
878
- // ---------- chart ----------
879
- // A flat, fixed-height plot: two of these panels then fit on one screen.
880
- const CHART_H = 400;
881
- const M = { l: 62, r: 18, t: 14, b: 52 };
882
-
883
- function render() {
884
- const metric = metricByKey[metricKey];
885
- svg.textContent = "";
886
- const W = Math.max(360, Math.round(box.clientWidth));
887
- const H = CHART_H;
888
- svg.setAttribute("width", W);
889
- svg.setAttribute("height", H);
890
- // Keep the chip list from outgrowing the chart beside it.
891
- chipsBox.style.maxHeight = Math.max(170, H - controlsBox.offsetHeight + 20) + "px";
892
-
893
- // x scale (log)
894
- const xsAll = POINTS.map(p => p[xKey]);
895
- const xmin = Math.min(...xsAll) * 0.65, xmax = Math.max(...xsAll) * 1.6;
896
- const lx0 = Math.log10(xmin), lx1 = Math.log10(xmax);
897
- const X = v => M.l + (Math.log10(v) - lx0) / (lx1 - lx0) * (W - M.l - M.r);
898
-
899
- const vals = POINTS.map(p => mval(p, metric));
900
- let y0, y1;
901
- if (metric.fromZero) {
902
- y0 = 0; y1 = Math.max(...vals) * 1.07;
903
- } else {
904
- const pad = (Math.max(...vals) - Math.min(...vals)) * 0.07;
905
- y0 = Math.min(...vals) - pad; y1 = Math.max(...vals) + pad;
906
- }
907
- const Y = v => M.t + (1 - (v - y0) / (y1 - y0)) * (H - M.t - M.b);
908
-
909
- // grid + axes
910
- const grid = el("g", {}, svg);
911
- for (let e = Math.ceil(lx0); Math.pow(10, e) < xmax; e++) {
912
- const gx = X(Math.pow(10, e));
913
- el("line", { x1: gx, y1: M.t, x2: gx, y2: H - M.b, stroke: "var(--line)", "stroke-width": 1 }, grid);
914
- const lbl = fmtNum(Math.pow(10, e), e >= 0 ? 0 : -e);
915
- el("text", { x: gx, y: H - M.b + 20, "text-anchor": "middle", "font-size": 12.5, fill: "var(--muted)" }, grid)
916
- .textContent = lbl;
917
- }
918
- for (const yv of ticks(y0, y1, 6)) {
919
- const gy = Y(yv);
920
- el("line", { x1: M.l, y1: gy, x2: W - M.r, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
921
- el("text", { x: M.l - 8, y: gy + 4, "text-anchor": "end", "font-size": 12.5, fill: "var(--muted)" }, grid)
922
- .textContent = fmtNum(yv, Number.isInteger(yv) ? 0 : metric.decimals);
923
- }
924
- el("rect", { x: M.l, y: M.t, width: W - M.l - M.r, height: H - M.t - M.b, fill: "none", stroke: "var(--line)" }, grid);
925
- el("text", {
926
- x: (M.l + W - M.r) / 2, y: H - 10, "text-anchor": "middle", "font-size": 14,
927
- "font-weight": 650, fill: "var(--ink)",
928
- }, grid).textContent = X_AXIS.axisLabel;
929
- el("text", {
930
- x: 0, y: 0, "text-anchor": "middle", "font-size": 14, "font-weight": 650, fill: "var(--ink)",
931
- transform: `translate(16 ${(M.t + H - M.b) / 2}) rotate(-90)`,
932
- }, grid).textContent = metric.axisLabel;
933
-
934
- drawOptimalArrow(grid, metric.lowerBetter, M, W, H);
935
-
936
- // pareto front (always shown)
937
- const front = computeFront(metric);
938
- const fv = front.verts;
939
- if (fv.length) {
940
- let d = `M${X(fv[0][0])},${metric.lowerBetter ? M.t : H - M.b}`;
941
- for (const [fx, fy] of fv) d += ` L${X(fx)},${Y(fy)}`;
942
- d += ` L${W - M.r},${Y(fv[fv.length - 1][1])}`;
943
- el("path", { d, fill: "none", stroke: "var(--ink)", "stroke-width": 1.6, "stroke-dasharray": "7 5", opacity: 0.85 }, svg);
944
- }
945
-
946
- const isOn = m => state.active.has(m);
947
-
948
- // connectors: variant links (scatter) / the trajectory itself
949
- const conn = el("g", {}, svg);
950
- for (const [method, pts] of byMethod) {
951
- if (pts.length < 2) continue;
952
- const on = isOn(method);
953
- if (!TRAJECTORY && !on) continue; // scatter: connectors only for active methods
954
- const dd = pts.map((p, i) => `${i ? "L" : "M"}${X(p[xKey])},${Y(mval(p, metric))}`).join(" ");
955
- el("path", {
956
- d: dd, fill: "none",
957
- stroke: on ? FAM_VAR[pts[0].family] : "var(--pt-muted)",
958
- "stroke-width": on ? (TRAJECTORY ? 2 : 1.4) : 1,
959
- opacity: on ? 0.6 : 0.35,
960
- "data-m": method,
961
- }, conn);
962
- }
963
-
964
- // points: inactive first, active on top
965
- const ptsOff = el("g", {}, svg);
966
- const ptsOn = el("g", {}, svg);
967
- for (const [method, pts] of byMethod) {
968
- const on = isOn(method);
969
- for (const p of pts) {
970
- const color = on ? FAM_VAR[p.family] : "var(--pt-muted)";
971
- const size = (on ? 7 : 5) * (TRAJECTORY ? 0.8 : 1);
972
- const op = on ? 0.95 : 0.5;
973
- drawMark(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), p.variant, color, size, op, p.method, on);
974
- // Imputation ring: every affected point in scatter mode; only the
975
- // trajectory's end point in trajectory mode (a ring on all ~8 line
976
- // points would read as beads, and the chip's ‡ already flags the line).
977
- if (p.imputed && (!TRAJECTORY || p === pts[pts.length - 1])) {
978
- drawImputedRing(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), size, color, op, p.method);
979
- }
980
- }
981
- }
982
-
983
- // labels for active methods at their best point, greedy de-overlap
984
- const labels = [];
985
- for (const [method, pts] of byMethod) {
986
- if (!isOn(method)) continue;
987
- const best = pts.reduce((a, b) =>
988
- (metric.lowerBetter ? mval(a, metric) < mval(b, metric) : mval(a, metric) > mval(b, metric)) ? a : b);
989
- labels.push({ method, family: best.family, x: X(best[xKey]) + 10, y: Y(mval(best, metric)) - 10 });
990
- }
991
- labels.sort((a, b) => a.y - b.y);
992
- for (let i = 1; i < labels.length; i++) {
993
- for (let j = 0; j < i; j++) {
994
- if (Math.abs(labels[i].x - labels[j].x) < 110 && Math.abs(labels[i].y - labels[j].y) < 15) {
995
- labels[i].y = labels[j].y + 15;
996
- }
997
- }
998
- }
999
- const lg = el("g", {}, svg);
1000
- for (const l of labels) {
1001
- const t = el("text", {
1002
- x: Math.min(l.x, W - M.r - 8), y: Math.max(l.y, M.t + 12), "font-size": 13, "font-weight": 700,
1003
- fill: FAM_VAR[l.family], "paint-order": "stroke", stroke: "var(--card)", "stroke-width": 3.5,
1004
- "text-anchor": l.x > W - 120 ? "end" : "start",
1005
- }, lg);
1006
- t.textContent = l.method;
1007
- }
1008
-
1009
- // invisible hit targets on top (bigger than marks)
1010
- const hits = el("g", {}, svg);
1011
- for (const p of POINTS) {
1012
- const h = el("circle", { cx: X(p[xKey]), cy: Y(mval(p, metric)), r: 12, fill: "transparent", cursor: "pointer" }, hits);
1013
- h.addEventListener("mouseenter", ev => showTip(p, ev));
1014
- h.addEventListener("mousemove", ev => tip.move(ev));
1015
- h.addEventListener("mouseleave", () => hideTip(p.method));
1016
- h.addEventListener("click", () => toggle(p.method));
1017
- }
1018
- }
1019
-
1020
- // Temporary hover emphasis without a re-render (a re-render would replace
1021
- // the hit node under the cursor mid-hover).
1022
- function emphasize(method, on) {
1023
- svg.querySelectorAll(`[data-m="${CSS.escape(method)}"]`).forEach(n => {
1024
- if (on) {
1025
- if (!n.dataset.save) n.dataset.save = n.getAttribute("opacity") || "1";
1026
- n.setAttribute("opacity", "0.95");
1027
- } else if (n.dataset.save) {
1028
- n.setAttribute("opacity", n.dataset.save);
1029
- delete n.dataset.save;
1030
- }
1031
- });
1032
- }
1033
-
1034
- function showTip(p, ev) {
1035
- emphasize(p.method, true);
1036
- const sub = TRAJECTORY ? (p.n_configs != null ? `${p.n_configs} configs` : "") : (p.variant || "");
1037
- let html = `<div class="t-name">${p.method}` + (sub ? ` <span class="t-var">(${sub})</span>` : "") + "</div>" +
1038
- `<div>${p.family}</div>`;
1039
- for (const m of METRICS) {
1040
- html += `<div>${m.label}: <b>${fmtMetric(m, mval(p, m))}</b></div>`;
1041
- }
1042
- html += `<div>${X_AXIS.short}: <b>${fmtTime(p[xKey])}</b></div>`;
1043
- if (p.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(p.imputed_pct, 0)}% of datasets</div>`;
1044
- tip.show(html, ev);
1045
- }
1046
- function hideTip(method) {
1047
- emphasize(method, false);
1048
- tip.hide();
1049
- }
1050
-
1051
- // ---------- chips ----------
1052
- const chipByMethod = new Map();
1053
- const famChips = new Map();
1054
- function familyMethods(fam) {
1055
- return [...byMethod.keys()].filter(m => byMethod.get(m)[0].family === fam);
1056
- }
1057
- // Chips are listed by leaderboard rank — best Elo first when Elo is
1058
- // configured, otherwise best value of the primary metric.
1059
- const RANK_METRIC = metricByKey["elo"] || metricByKey[METRICS[0].key];
1060
- function bestVal(method) {
1061
- const vals = byMethod.get(method).map(p => mval(p, RANK_METRIC));
1062
- return RANK_METRIC.lowerBetter ? Math.min(...vals) : Math.max(...vals);
1063
- }
1064
- function rankSorted(methods) {
1065
- return [...methods].sort((a, b) =>
1066
- RANK_METRIC.lowerBetter ? bestVal(a) - bestVal(b) : bestVal(b) - bestVal(a));
1067
- }
1068
- function buildChips() {
1069
- for (const fam of FAM_ORDER) {
1070
- const methods = rankSorted(familyMethods(fam));
1071
- if (!methods.length) continue;
1072
- const row = document.createElement("div");
1073
- row.className = "chiprow";
1074
- const famBtn = document.createElement("button");
1075
- famBtn.className = "famchip";
1076
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1077
- famBtn.innerHTML = famChipLabel(fam, methods.length);
1078
- famBtn.title = `Toggle all ${methods.length} ${fam} methods`;
1079
- famBtn.addEventListener("click", () => toggleFamily(fam));
1080
- row.appendChild(famBtn);
1081
- famChips.set(fam, famBtn);
1082
- const set = document.createElement("div");
1083
- set.className = "chipset";
1084
- for (const m of methods) {
1085
- const b = document.createElement("button");
1086
- b.className = "chip";
1087
- b.style.setProperty("--fam", FAM_VAR[fam]);
1088
- const imputed = byMethod.get(m).some(p => p.imputed);
1089
- const label = document.createElement("span");
1090
- label.textContent = m;
1091
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1092
- b.appendChild(label);
1093
- if (imputed) {
1094
- const mark = document.createElement("span");
1095
- mark.className = "imp-mark";
1096
- mark.textContent = "‡";
1097
- b.appendChild(mark);
1098
- }
1099
- b.title = m + (imputed ? " — partially imputed" : "");
1100
- b.addEventListener("click", () => toggle(m));
1101
- set.appendChild(b);
1102
- chipByMethod.set(m, b);
1103
- }
1104
- row.appendChild(set);
1105
- chipsBox.appendChild(row);
1106
- }
1107
- }
1108
- function syncChips() {
1109
- for (const [m, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.active.has(m)));
1110
- for (const [fam, b] of famChips) {
1111
- b.setAttribute("aria-pressed", String(familyMethods(fam).every(m => state.active.has(m))));
1112
- }
1113
- }
1114
- function toggle(m) {
1115
- if (state.active.has(m)) state.active.delete(m); else state.active.add(m);
1116
- syncChips();
1117
- render();
1118
- }
1119
- function toggleFamily(fam) {
1120
- const methods = familyMethods(fam);
1121
- const allOn = methods.every(m => state.active.has(m));
1122
- for (const m of methods) {
1123
- if (allOn) state.active.delete(m); else state.active.add(m);
1124
- }
1125
- syncChips();
1126
- render();
1127
- }
1128
- function setActive(methods) {
1129
- state.active = new Set(methods);
1130
- syncChips();
1131
- render();
1132
- }
1133
- document.getElementById("btn-front").addEventListener("click",
1134
- () => setActive(computeFront(metricByKey[metricKey]).methods));
1135
- document.getElementById("btn-all").addEventListener("click", () => setActive([...byMethod.keys()]));
1136
- document.getElementById("btn-none").addEventListener("click", () => setActive([]));
1137
-
1138
- // metric selector (hidden when only one metric is configured)
1139
- const metricPick = document.getElementById("metricpick");
1140
- const metricSelect = document.getElementById("metric-select");
1141
- if (METRICS.length > 1) {
1142
- metricPick.hidden = false;
1143
- for (const m of METRICS) {
1144
- const opt = document.createElement("option");
1145
- opt.value = m.key;
1146
- opt.textContent = m.label;
1147
- metricSelect.appendChild(opt);
1148
- }
1149
- metricSelect.addEventListener("change", ev => {
1150
- metricKey = ev.target.value;
1151
- render();
1152
- });
1153
- }
1154
-
1155
- // ---------- legend strip ----------
1156
- function buildLegend() {
1157
- const box2 = document.getElementById("legendstrip");
1158
- let html = "";
1159
- if (!TRAJECTORY) {
1160
- html +=
1161
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><circle cx="7" cy="7" r="5" fill="var(--muted)"/></svg> Default</span>' +
1162
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" rx="1.5" fill="var(--muted)"/></svg> Tuned</span>' +
1163
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><path d="M3,3 L11,11 M3,11 L11,3" stroke="var(--muted)" stroke-width="2.6" stroke-linecap="round"/></svg> Tuned + Ensembled</span>';
1164
- } else {
1165
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--muted)" stroke-width="2"/><circle cx="6" cy="4" r="2.6" fill="var(--muted)"/><circle cx="16" cy="4" r="2.6" fill="var(--muted)"/></svg> Tuning trajectory (more configs &rarr; more time)</span>';
1166
- }
1167
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--ink)" stroke-width="1.6" stroke-dasharray="6 4"/></svg> Pareto front (always shown)</span>';
1168
- if (POINTS.some(p => p.imputed)) {
1169
- html += '<span class="item"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="4" fill="var(--muted)"/><circle cx="9" cy="9" r="7.5" fill="none" stroke="var(--muted)" stroke-width="1.3" stroke-dasharray="3 2.5"/></svg> &Dagger; partially imputed</span>';
1170
- }
1171
- // Model family, named: highlighted points are colored by family, and in paper
1172
- // view the chip list that would otherwise decode them is hidden.
1173
- const families = FAM_ORDER.filter(f => POINTS.some(p => p.family === f));
1174
- if (families.length > 1) {
1175
- html += '<span class="legendbreak"></span><span class="item">Family:</span>';
1176
- for (const fam of families) {
1177
- html += `<span class="item"><svg width="12" height="12" viewBox="0 0 12 12">` +
1178
- `<circle cx="6" cy="6" r="5" fill="${FAM_VAR[fam]}"/></svg> ` +
1179
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`;
1180
- }
1181
- }
1182
- box2.innerHTML = html;
1183
- }
1184
-
1185
- // ---------- paper view ----------
1186
- setUpPaperView(render);
1187
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(document.title));
1188
-
1189
- // ---------- data table ----------
1190
- function buildTable() {
1191
- const m0 = metricByKey[METRICS[0].key];
1192
- const rows = [...POINTS].sort((a, b) =>
1193
- m0.lowerBetter ? mval(a, m0) - mval(b, m0) : mval(b, m0) - mval(a, m0));
1194
- let html = "<table><thead><tr><th>Method</th>";
1195
- html += TRAJECTORY ? "<th>Configs</th>" : "<th>Variant</th>";
1196
- html += "<th>Family</th>";
1197
- for (const m of METRICS) html += `<th>${m.label}</th>`;
1198
- html += `<th>${X_AXIS.short}</th><th>Imputed</th></tr></thead><tbody>`;
1199
- for (const p of rows) {
1200
- html += `<tr><td>${p.method}</td><td>${TRAJECTORY ? (p.n_configs != null ? p.n_configs : "—") : p.variant}</td><td>${p.family}</td>`;
1201
- for (const m of METRICS) html += `<td>${fmtMetric(m, mval(p, m))}</td>`;
1202
- html += `<td>${fmtNum(p[xKey], 3)}</td>`;
1203
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1204
- }
1205
- html += "</tbody></table>";
1206
- document.getElementById("tblwrap").innerHTML = html;
1207
- }
1208
-
1209
- const _renderInner = render;
1210
- render = function () {
1211
- _renderInner();
1212
- postHeight();
1213
- };
1214
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1215
- let resizeTimer = null;
1216
- window.addEventListener("resize", () => {
1217
- clearTimeout(resizeTimer);
1218
- resizeTimer = setTimeout(render, 120);
1219
- });
1220
- window.addEventListener("load", postHeight);
1221
-
1222
- buildChips();
1223
- buildLegend();
1224
- buildTable();
1225
- syncChips();
1226
- render();
1227
- })();
1228
- </script>
1229
- </body>
1230
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_improvability_vs_time_infer.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:2d4ed0a9b275cd8602f4dbb38201fd6cc8918d6896ec8e750f4a8cb6d2d2196d
3
- size 264726
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/pareto_front_points.csv DELETED
@@ -1,63 +0,0 @@
1
- method,variant,family,x_infer,x_train,imp,elo,imputed,imputed_pct
2
- TabFM,Default,Foundation Model,6.985429071794801,38.805908867807105,4.074482364796911,1791.7,False,0.0
3
- TabPFN-3,Default,Foundation Model,0.3988194969093643,3.6584650300170622,9.32829663379356,1665.3,False,0.0
4
- TabPFN-2.6,Default,Foundation Model,0.5548034626013535,5.482579877802832,10.750545524440877,1619.8,False,0.0
5
- RealTabPFN-2.5,Tuned + Ens.,Foundation Model,8.907660220396073,2040.2228420972824,10.4265657302182,1594.9,False,0.0
6
- TabICLv2,Default,Foundation Model,0.15054921462491172,2.0524528171730156,10.175153831453988,1593.7,False,0.0
7
- RealTabPFN-2.5,Tuned,Foundation Model,1.217878699427239,2040.2228420972824,11.11614833993503,1554.3,False,0.0
8
- RealTabPFN-2.5,Default,Foundation Model,0.644969598113518,5.809798529870024,11.66045120270563,1524.4,False,0.0
9
- RealMLP,Tuned + Ens.,Neural Network,11.974985376498216,2950.724447694827,12.881492492932924,1507.9,False,0.0
10
- TabDPT-Turbo,Default,Foundation Model,0.18241940288749647,2.06768405148355,13.999843249302529,1459.4,False,0.0
11
- TabDPT,Tuned + Ens.,Foundation Model,286.53737883567806,4910.377306237253,13.787925083199838,1457.8,False,0.0
12
- TabM,Tuned + Ens.,Neural Network,2.246865858634313,2450.1270774379373,14.080621083598398,1450.2,False,0.0
13
- LightGBM,Tuned + Ens.,Tree-based,2.638673830958025,417.0474735648702,15.033496734155674,1432.9,False,0.0
14
- RealMLP,Tuned,Neural Network,0.663132054971364,2950.724447694827,14.202181797556316,1426.4,False,0.0
15
- CatBoost,Tuned + Ens.,Tree-based,0.3442282032056243,1346.210691180287,14.655679872953623,1421.5,False,0.0
16
- CatBoost,Tuned,Tree-based,0.03809286543601384,1346.210691180287,14.885063048465241,1408.1,False,0.0
17
- iLTM,Tuned + Ens.,Foundation Model,464.3696034625343,12685.081550183375,15.303555132389759,1404.5,False,0.0
18
- TabDPT,Tuned,Foundation Model,39.95973646029047,4910.377306237253,14.936053240835797,1402.6,False,0.0
19
- TabM,Tuned,Neural Network,0.25129394622912726,2450.1270774379373,14.951713350982976,1393.6,False,0.0
20
- ModernNCA,Tuned + Ens.,Neural Network,8.137916867537678,4621.665633563503,15.598545807661889,1389.4,False,0.0
21
- LightGBM,Tuned,Tree-based,0.3337603531487198,417.0474735648702,15.687611694327014,1385.7,False,0.0
22
- CatBoost,Default,Tree-based,0.02489617665608724,5.876676442022475,15.524440818146829,1378.5,False,0.0
23
- XGBoost,Tuned + Ens.,Tree-based,1.6894451629541256,693.4907982506384,15.753819542791774,1375.0,False,0.0
24
- LimiX,Default,Foundation Model,6.2423742585450634,26.459751268166386,15.526302545921503,1362.6,False,0.0
25
- ModernNCA,Tuned,Neural Network,0.4747242314576746,4621.665633563503,16.043959051908736,1355.2,False,0.0
26
- TabSwift,Default,Foundation Model,0.07196837694453946,1.1753394519011986,16.080743288565568,1350.4,False,0.0
27
- XGBoost,Tuned,Tree-based,0.3080760155405317,693.4907982506384,16.065044325649545,1350.1,False,0.0
28
- xRFM,Tuned + Ens.,Other,2.549259534903935,846.8860702336882,16.284217975386294,1347.6,False,0.0
29
- ChimeraBoost,Tuned + Ens.,Tree-based,0.4040760969279745,427.39175038402846,17.212551464527458,1333.4,False,0.0
30
- TabDPT,Default,Foundation Model,43.737812222627376,47.64668788909912,17.225282770201353,1324.7,False,0.0
31
- ChimeraBoost,Tuned,Tree-based,0.033651277628640924,427.39175038402846,17.551093954280812,1308.4,False,0.0
32
- xRFM,Tuned,Other,0.13027435019228545,846.8860702336882,17.449220868675354,1300.2,False,0.0
33
- TabM,Default,Neural Network,0.2435252328360982,7.486463968520102,17.00014333184639,1297.6,False,0.0
34
- iLTM,Tuned,Foundation Model,62.129902117180094,12685.081550183375,17.147247379128842,1295.0,False,0.0
35
- TorchMLP,Tuned + Ens.,Neural Network,1.9516254299583915,2875.520340774675,17.161152358690444,1285.2,False,0.0
36
- SAP-RPT-OSS,Default,Foundation Model,2.0745579697490073,14.11326684554418,18.278320508713314,1283.4,False,0.0
37
- EBM,Tuned + Ens.,Tree-based,0.14129001235465857,1711.2458519871225,18.672215711732292,1271.5,False,0.0
38
- ChimeraBoost,Default,Tree-based,0.032584832539708695,1.9886321895080843,18.657087753455368,1263.9,False,0.0
39
- ModernNCA,Default,Neural Network,0.3073466783584545,14.869686344646652,19.19422942055122,1248.1,False,0.0
40
- EBM,Tuned,Tree-based,0.015030898720760692,1711.2458519871225,19.313668280627464,1235.3,False,0.0
41
- RealMLP,Default,Neural Network,1.688795494836301,10.05564923800873,18.569226672945717,1229.7,False,0.0
42
- XGBoost,Default,Tree-based,0.12262328807300621,1.9409389396340444,18.792223322104178,1214.2,False,0.0
43
- TorchMLP,Tuned,Neural Network,0.131112832826372,2875.520340774675,18.87687891401735,1211.5,False,0.0
44
- ExtraTrees,Tuned + Ens.,Tree-based,0.7662402324832739,263.0379305178949,19.777389199117614,1208.5,False,0.0
45
- FastaiMLP,Tuned + Ens.,Neural Network,4.466695070266724,593.237788402893,19.676871164178753,1207.0,False,0.0
46
- EBM,Default,Tree-based,0.014352864847965238,6.673146850898109,20.25536331915339,1203.8,False,0.0
47
- LightGBM,Default,Tree-based,0.14167879422505697,1.9600326879612946,19.344291592086606,1186.4,False,0.0
48
- ExtraTrees,Tuned,Tree-based,0.07121299392949774,263.0379305178949,20.563808515074136,1180.5,False,0.0
49
- RandomForest,Tuned + Ens.,Tree-based,0.7709478321252661,373.23710550957964,20.828476433186648,1170.8,False,0.0
50
- FastaiMLP,Tuned,Neural Network,0.306391541190021,593.237788402893,21.096318521849227,1146.9,False,0.0
51
- RandomForest,Tuned,Tree-based,0.08522791862487794,373.23710550957964,21.53504578065053,1137.1,False,0.0
52
- TabSTAR,Tuned,Foundation Model,4.26805346323971,28729.737365569254,25.584307557024438,1097.3,False,0.0
53
- TabSTAR,Tuned + Ens.,Foundation Model,18.8114333152771,28729.737365569254,25.600075428140812,1096.0,False,0.0
54
- iLTM,Default,Foundation Model,68.17329693701195,296.63929176158115,23.023643328901947,1094.5,False,0.0
55
- PerpetualBooster,Tuned + Ens.,Tree-based,0.5997539679209392,185.306705652078,26.44564252568807,1088.7,False,0.0
56
- TorchMLP,Default,Neural Network,0.1258046787872722,9.99256321379882,22.815130870764776,1074.3,False,0.0
57
- PerpetualBooster,Tuned,Tree-based,0.2581384468078613,185.306705652078,27.8016434929848,1050.3,False,0.0
58
- xRFM,Default,Other,0.9186215875564591,3.2328671519382346,26.44486339439917,1043.4,False,0.0
59
- ExtraTrees,Default,Tree-based,0.28340813208977944,1.8719729433204921,26.176496328475334,1009.0,False,0.0
60
- FastaiMLP,Default,Neural Network,0.37317813888351864,2.856149195889622,25.681854522852465,1007.8,False,0.0
61
- RandomForest,Default,Tree-based,0.05354057584727896,0.43432508267329906,26.26500423038912,1000.0,False,0.0
62
- TabSTAR,Default,Foundation Model,5.348086842145453,384.74563018139656,30.455609940739038,993.3,False,0.0
63
- PerpetualBooster,Default,Tree-based,0.028580506642659504,27.322832322961396,31.748055193831643,936.2,False,0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/pareto_n_configs_imp.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:036d780a68a23a2cff1d510eff9811401d88a26d7c4bad96d0ef045517bfa96a
3
- size 293312
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/tuning-impact-elo.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:2a267fd19bf63d233f1b8548b957a1c2486976a3da3ecf03e3879cf153f323cc
3
- size 105318
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/tuning_trajectories.csv DELETED
@@ -1,163 +0,0 @@
1
- method,family,x_train,x_infer,imp,elo,n_configs,imputed,imputed_pct
2
- CatBoost,Tree-based,5.876676442022475,0.02489617665608724,15.545773630432649,1392.9,1.0,False,0.0
3
- CatBoost,Tree-based,11.667825125586923,0.06336535482356946,15.118241955137155,1413.1,2.0,False,0.0
4
- CatBoost,Tree-based,31.910651762403862,0.11771650741480857,14.848578649025981,1430.8,5.0,False,0.0
5
- CatBoost,Tree-based,62.57212517570079,0.1714353465164701,14.747756855813183,1435.8,10.0,False,0.0
6
- CatBoost,Tree-based,155.90493556689327,0.22162205257045137,14.640617769474321,1441.2,25.0,False,0.0
7
- CatBoost,Tree-based,317.34591689097914,0.2666794694644051,14.63114463814291,1441.7,50.0,False,0.0
8
- CatBoost,Tree-based,662.4830210733933,0.31052760007315267,14.647846561269168,1440.2,100.0,False,0.0
9
- CatBoost,Tree-based,998.8498259224127,0.340735055101516,14.667356369432946,1438.8,150.0,False,0.0
10
- CatBoost,Tree-based,1346.210691180287,0.344857002822471,14.674240095020256,1437.7,201.0,False,0.0
11
- ChimeraBoost,Tree-based,1.9886321895080843,0.032584832539708695,18.678063113906685,1273.2,1.0,False,0.0
12
- ChimeraBoost,Tree-based,4.43838502030943,0.06094444009053425,18.336632808534546,1294.1,2.0,False,0.0
13
- ChimeraBoost,Tree-based,10.070184857413143,0.1129020180967119,18.11679730595953,1307.5,5.0,False,0.0
14
- ChimeraBoost,Tree-based,20.473417816445547,0.17710346910688612,17.925973974729047,1318.0,10.0,False,0.0
15
- ChimeraBoost,Tree-based,52.71737575557528,0.24549033666046208,17.653702793765603,1330.8,25.0,False,0.0
16
- ChimeraBoost,Tree-based,106.3941874905326,0.2926890222079797,17.502292085061757,1338.7,50.0,False,0.0
17
- ChimeraBoost,Tree-based,212.82074782052993,0.3120254129614995,17.33931475498968,1346.0,100.0,False,0.0
18
- ChimeraBoost,Tree-based,318.99571798382766,0.3237207715925697,17.270003424423187,1348.8,150.0,False,0.0
19
- ChimeraBoost,Tree-based,427.39175038402846,0.4040760969279745,17.232645726102113,1351.7,201.0,False,0.0
20
- EBM,Tree-based,6.673146850898109,0.014352864847965238,20.275933791848207,1208.2,1.0,False,0.0
21
- EBM,Tree-based,16.576534345676816,0.030424724199585355,19.878683743638497,1222.7,2.0,False,0.0
22
- EBM,Tree-based,47.738558903301914,0.052842477784541445,19.453429661489196,1243.5,5.0,False,0.0
23
- EBM,Tree-based,89.41107246991604,0.0702878348384009,19.272670456493394,1252.8,10.0,False,0.0
24
- EBM,Tree-based,218.44812470797967,0.09747169882795435,19.043819281561163,1263.4,25.0,False,0.0
25
- EBM,Tree-based,440.25457359654257,0.11411218718429054,18.902341183605127,1271.7,50.0,False,0.0
26
- EBM,Tree-based,856.7647171052814,0.12772155113709277,18.78039180099042,1278.4,100.0,False,0.0
27
- EBM,Tree-based,1266.0910927829382,0.13762164045367722,18.736984316643394,1281.9,150.0,False,0.0
28
- EBM,Tree-based,1711.2458519871225,0.1384442291079833,18.69454550084852,1284.9,201.0,False,0.0
29
- ExtraTrees,Tree-based,1.8719729433204921,0.28340813208977944,26.19484841286617,1009.7,1.0,False,0.0
30
- ExtraTrees,Tree-based,3.1259596547771116,0.299267558256785,24.34885423133725,1053.5,2.0,False,0.0
31
- ExtraTrees,Tree-based,6.772191225672007,0.3233863551370985,21.682680228814046,1124.8,5.0,False,0.0
32
- ExtraTrees,Tree-based,13.288092341970305,0.39167570468287294,20.760558802976053,1165.9,10.0,False,0.0
33
- ExtraTrees,Tree-based,32.8419339595057,0.4883309222469785,20.10698573925726,1197.4,25.0,False,0.0
34
- ExtraTrees,Tree-based,65.49386348647792,0.56779316986261,19.944577260601946,1207.8,50.0,False,0.0
35
- ExtraTrees,Tree-based,130.83697151858863,0.6638268741052196,19.834039933936086,1214.7,100.0,False,0.0
36
- ExtraTrees,Tree-based,196.22448049705483,0.7241674769499868,19.791012646492206,1218.9,150.0,False,0.0
37
- ExtraTrees,Tree-based,263.0379305178949,0.7662402324832739,19.798451388325567,1219.9,201.0,False,0.0
38
- FastaiMLP,Neural Network,2.856149195889622,0.37317813888351864,25.701345794106835,1012.4,1.0,False,0.0
39
- FastaiMLP,Neural Network,5.5343795003616165,0.6641420918672822,24.502014126684376,1049.9,2.0,False,0.0
40
- FastaiMLP,Neural Network,14.55103442753632,1.1863927783450354,22.653470439457397,1116.6,5.0,False,0.0
41
- FastaiMLP,Neural Network,29.7233431539627,1.7323440480232237,21.746441627597715,1149.9,10.0,False,0.0
42
- FastaiMLP,Neural Network,72.5444980812019,2.392081415581216,20.845819728470904,1180.3,25.0,False,0.0
43
- FastaiMLP,Neural Network,145.75437119097205,3.1392189600815126,20.39162631932187,1196.9,50.0,False,0.0
44
- FastaiMLP,Neural Network,293.94750082788664,3.8048101374308265,20.04889167045696,1207.2,100.0,False,0.0
45
- FastaiMLP,Neural Network,441.8525399102105,4.207369513432185,19.826636643046832,1215.4,150.0,False,0.0
46
- FastaiMLP,Neural Network,593.237788402893,4.45581943154335,19.693526906883832,1220.6,201.0,False,0.0
47
- LightGBM,Tree-based,1.9600326879612946,0.14167879422505697,19.36479217453779,1188.7,1.0,False,0.0
48
- LightGBM,Tree-based,4.146536889124949,0.6924533471949544,17.853666842868513,1267.1,2.0,False,0.0
49
- LightGBM,Tree-based,10.390743356824784,0.9306682768676963,16.68551387028264,1346.2,5.0,False,0.0
50
- LightGBM,Tree-based,20.270167537261766,1.197546153728451,16.08404982178837,1386.9,10.0,False,0.0
51
- LightGBM,Tree-based,51.48744833401502,1.50753196069901,15.52809643468747,1421.6,25.0,False,0.0
52
- LightGBM,Tree-based,103.68557420134589,1.674099392656769,15.31584330152404,1437.2,50.0,False,0.0
53
- LightGBM,Tree-based,207.54351147839122,2.0960239899208046,15.155215757412046,1447.7,100.0,False,0.0
54
- LightGBM,Tree-based,310.2926532568141,2.2957707359829054,15.093355735444023,1451.2,150.0,False,0.0
55
- LightGBM,Tree-based,417.0474735648702,2.638673830958025,15.056180468340754,1453.8,201.0,False,0.0
56
- LimiX,Foundation Model,26.459751268166386,6.2423742585450634,15.4843630348554,1381.2,1.0,False,0.0
57
- ModernNCA,Neural Network,14.869686344646652,0.3073466783584545,19.214634854684004,1258.8,1.0,False,0.0
58
- ModernNCA,Neural Network,40.51716877036181,0.8515440487861633,17.923544469309995,1296.1,2.0,False,0.0
59
- ModernNCA,Neural Network,111.44983108853623,1.746831801986318,16.005200974400687,1381.4,5.0,False,0.0
60
- ModernNCA,Neural Network,224.92944042496907,2.5416509890556336,15.514065161520602,1406.1,10.0,False,0.0
61
- ModernNCA,Neural Network,554.8880523029388,4.032296481132508,15.005455193102883,1431.4,25.0,False,0.0
62
- ModernNCA,Neural Network,1145.4516650488413,5.448522539138794,15.052960698398241,1428.7,50.0,False,0.0
63
- ModernNCA,Neural Network,2295.4928354707367,6.931406373977661,15.312154787900578,1419.9,100.0,False,0.0
64
- ModernNCA,Neural Network,3433.7924746693766,7.728596683131394,15.511417949160927,1411.9,150.0,False,0.0
65
- ModernNCA,Neural Network,4621.665633563503,8.137916867537678,15.625323263302192,1406.7,201.0,False,0.0
66
- PerpetualBooster,Tree-based,27.322832322961396,0.028580506642659504,32.25100598233364,936.7,1.0,False,0.0
67
- PerpetualBooster,Tree-based,60.33046553317705,0.23376323541005453,30.003028571926745,1001.6,2.0,False,0.0
68
- PerpetualBooster,Tree-based,157.39589541510517,0.5732905745506287,26.632005416466946,1088.2,5.0,False,0.0
69
- PerpetualBooster,Tree-based,185.306705652078,0.5997539679209392,26.460216259374146,1095.6,6.0,False,0.0
70
- RandomForest,Tree-based,0.43432508267329906,0.05354057584727896,26.293240248827328,999.4,1.0,False,0.0
71
- RandomForest,Tree-based,2.300696812702446,0.11877280473082547,23.63129314546007,1058.9,2.0,False,0.0
72
- RandomForest,Tree-based,7.869405165361387,0.23579108802464072,22.294500271065914,1101.7,5.0,False,0.0
73
- RandomForest,Tree-based,17.24737248948874,0.34062966640910297,21.59813362204917,1131.3,10.0,False,0.0
74
- RandomForest,Tree-based,45.0373615696637,0.5043278961482858,21.108236480738782,1162.1,25.0,False,0.0
75
- RandomForest,Tree-based,91.39172333285852,0.5878338983942606,20.976376645285406,1170.7,50.0,False,0.0
76
- RandomForest,Tree-based,184.69967416087664,0.6965050641895468,20.889389653111095,1176.8,100.0,False,0.0
77
- RandomForest,Tree-based,277.8099565315396,0.7595438384705165,20.8512167325917,1179.4,150.0,False,0.0
78
- RandomForest,Tree-based,373.23710550957964,0.7784693873625508,20.846353319166443,1181.1,201.0,False,0.0
79
- RealMLP,Neural Network,10.05564923800873,1.688795494836301,18.590239453484394,1235.3,1.0,False,0.0
80
- RealMLP,Neural Network,24.67055944083978,2.4593014940325313,16.23691942847831,1345.9,2.0,False,0.0
81
- RealMLP,Neural Network,69.64352733395047,3.7162198129739616,14.560737750122787,1438.2,5.0,False,0.0
82
- RealMLP,Neural Network,141.15299775889818,5.676482463380541,13.942792911633916,1472.7,10.0,False,0.0
83
- RealMLP,Neural Network,364.60114695980747,7.847718735760291,13.467173212944909,1502.2,25.0,False,0.0
84
- RealMLP,Neural Network,736.9754853515838,9.767676536105808,13.247150637288602,1515.4,50.0,False,0.0
85
- RealMLP,Neural Network,1469.265397304651,11.259146773020426,13.064776423132237,1524.1,100.0,False,0.0
86
- RealMLP,Neural Network,2197.532004168663,12.053178920512211,12.972203262678384,1529.9,150.0,False,0.0
87
- RealMLP,Neural Network,2950.724447694827,12.377824840296128,12.904667349438354,1532.8,201.0,False,0.0
88
- RealTabPFN-2.5,Foundation Model,5.809798529870024,0.644969598113518,11.683982092880285,1538.3,1.0,False,0.0
89
- RealTabPFN-2.5,Foundation Model,16.695089235760015,1.4031929555826625,11.614346808353798,1544.7,2.0,False,0.0
90
- RealTabPFN-2.5,Foundation Model,45.81806103711327,2.874227223348986,11.2924871479828,1561.1,5.0,False,0.0
91
- RealTabPFN-2.5,Foundation Model,96.05664533202847,4.09552534439249,11.111189934935469,1578.5,10.0,False,0.0
92
- RealTabPFN-2.5,Foundation Model,245.14109091684222,5.753407464362681,10.868476470961895,1599.1,25.0,False,0.0
93
- RealTabPFN-2.5,Foundation Model,498.5667116885384,6.850076117335025,10.686824052693943,1608.6,50.0,False,0.0
94
- RealTabPFN-2.5,Foundation Model,1015.1487598759433,7.792073605314185,10.568015672278301,1613.1,100.0,False,0.0
95
- RealTabPFN-2.5,Foundation Model,1526.7130408425285,8.999759905935408,10.507654206859593,1614.3,150.0,False,0.0
96
- RealTabPFN-2.5,Foundation Model,2040.2228420972824,9.030681926100565,10.455540657911111,1613.3,201.0,False,0.0
97
- SAP-RPT-OSS,Foundation Model,14.11326684554418,2.0745579697490073,18.2986171533795,1297.7,1.0,False,0.0
98
- TabDPT,Foundation Model,47.64668788909912,43.737812222627376,17.244714242411675,1337.6,1.0,False,0.0
99
- TabDPT,Foundation Model,70.40721927502358,57.29898182743639,16.427375964657816,1364.1,2.0,False,0.0
100
- TabDPT,Foundation Model,132.97854094385033,81.7804361753873,15.612978495749699,1399.5,5.0,False,0.0
101
- TabDPT,Foundation Model,243.77644681542583,113.41332740687801,15.25847431382481,1415.5,10.0,False,0.0
102
- TabDPT,Foundation Model,599.14526392591,170.40307060248648,14.58792821121514,1442.4,25.0,False,0.0
103
- TabDPT,Foundation Model,1224.6320092924832,211.92435767541818,14.246562277368833,1456.9,50.0,False,0.0
104
- TabDPT,Foundation Model,2440.704045474264,252.46699651351815,14.025515152987841,1467.0,100.0,False,0.0
105
- TabDPT,Foundation Model,3652.693914186981,264.8770310030767,13.88361683040771,1472.2,150.0,False,0.0
106
- TabDPT,Foundation Model,4910.377306237253,289.5741517328534,13.812001220438422,1474.3,201.0,False,0.0
107
- TabDPT-Turbo,Foundation Model,2.06768405148355,0.18241940288749647,14.0203361291699,1472.3,1.0,False,0.0
108
- TabFM,Foundation Model,38.805908867807105,6.985429071794801,4.0944582802657035,1807.7,1.0,False,0.0
109
- TabICLv2,Foundation Model,2.0524528171730156,0.15054921462491172,10.19897564153584,1611.8,1.0,False,0.0
110
- TabM,Neural Network,7.486463968520102,0.2435252328360982,17.02187952020658,1307.7,1.0,False,0.0
111
- TabM,Neural Network,16.686302531690117,0.4863020309973096,16.495752283556467,1334.9,2.0,False,0.0
112
- TabM,Neural Network,56.596063695315806,0.890570759276549,15.85723403452073,1370.8,5.0,False,0.0
113
- TabM,Neural Network,118.7995551510581,0.9696677240363341,15.285453852205777,1405.2,10.0,False,0.0
114
- TabM,Neural Network,301.71685986726413,1.2874446083056301,14.793023765894237,1431.2,25.0,False,0.0
115
- TabM,Neural Network,606.7957589349577,1.5958040707920327,14.504939209135886,1447.7,50.0,False,0.0
116
- TabM,Neural Network,1211.551032832957,1.9425270855949641,14.301458534985233,1459.4,100.0,False,0.0
117
- TabM,Neural Network,1826.0696865509133,2.1383865310957564,14.157021352875951,1465.8,150.0,False,0.0
118
- TabM,Neural Network,2450.1270774379373,2.236152469066152,14.101948299098552,1471.1,201.0,False,0.0
119
- TabPFN-2.6,Foundation Model,5.482579877802832,0.5548034626013535,10.773892289839509,1643.6,1.0,False,0.0
120
- TabPFN-3,Foundation Model,3.6584650300170622,0.3988194969093643,9.350273566614813,1683.7,1.0,False,0.0
121
- TabSTAR,Foundation Model,384.74563018139656,5.348086842145453,30.47412388440593,997.8,1.0,False,0.0
122
- TabSTAR,Foundation Model,579.5314694237709,6.864771890640259,30.347038882975998,1006.6,2.0,False,0.0
123
- TabSTAR,Foundation Model,986.9188087141514,7.884261027648034,30.080943935841937,1014.9,5.0,False,0.0
124
- TabSTAR,Foundation Model,1626.205745840524,9.437109846485178,29.421057045559767,1032.7,10.0,False,0.0
125
- TabSTAR,Foundation Model,3523.6508469471287,12.411686028639474,28.225052033106863,1055.9,25.0,False,0.0
126
- TabSTAR,Foundation Model,7090.437342984086,14.581168526013691,27.026195240095387,1076.7,50.0,False,0.0
127
- TabSTAR,Foundation Model,14236.603036992845,17.05862640937169,26.355370232204596,1092.3,100.0,False,0.0
128
- TabSTAR,Foundation Model,21385.328840252965,18.39983408008061,25.942905489351652,1103.1,150.0,False,0.0
129
- TabSTAR,Foundation Model,28729.737365569254,18.8114333152771,25.62018492142047,1114.2,201.0,False,0.0
130
- TabSwift,Foundation Model,1.1753394519011986,0.07196837694453946,16.10208326307144,1367.0,1.0,False,0.0
131
- TorchMLP,Neural Network,9.99256321379882,0.1258046787872722,22.82955610374213,1082.6,1.0,False,0.0
132
- TorchMLP,Neural Network,23.39767303078301,0.25026397500859937,21.109494917973674,1136.9,2.0,False,0.0
133
- TorchMLP,Neural Network,67.83522273002498,0.5082028825487114,19.58502204023533,1188.5,5.0,False,0.0
134
- TorchMLP,Neural Network,150.5961763502482,0.7461273476481438,18.859173580368292,1222.0,10.0,False,0.0
135
- TorchMLP,Neural Network,358.89142527924406,1.1062947766294757,18.17773359177303,1252.0,25.0,False,0.0
136
- TorchMLP,Neural Network,711.8242682675321,1.3608592505593902,17.762854174437347,1273.5,50.0,False,0.0
137
- TorchMLP,Neural Network,1402.3570975877033,1.5999240551180054,17.468497616393975,1287.5,100.0,False,0.0
138
- TorchMLP,Neural Network,2125.061118692563,1.7712720948858605,17.31173997878631,1296.2,150.0,False,0.0
139
- TorchMLP,Neural Network,2875.520340774675,1.9516254299583915,17.17786611550823,1302.7,201.0,False,0.0
140
- XGBoost,Tree-based,1.9409389396340444,0.12262328807300621,18.812692339626054,1218.2,1.0,False,0.0
141
- XGBoost,Tree-based,5.773697624577766,0.549105895360311,17.165162871678664,1301.3,2.0,False,0.0
142
- XGBoost,Tree-based,17.165802076190833,0.9903293256488429,16.632507902802494,1337.3,5.0,False,0.0
143
- XGBoost,Tree-based,34.081364319342484,1.180882272831562,16.31489777339226,1358.0,10.0,False,0.0
144
- XGBoost,Tree-based,86.41550386463979,1.5144136771170538,16.068815450472144,1372.9,25.0,False,0.0
145
- XGBoost,Tree-based,172.69993584479832,1.6869916704181176,15.96558821702624,1379.4,50.0,False,0.0
146
- XGBoost,Tree-based,344.2808053206447,1.7329947109908745,15.883217938316573,1385.7,100.0,False,0.0
147
- XGBoost,Tree-based,515.1408947247787,1.8097831769800434,15.823927924282033,1389.9,150.0,False,0.0
148
- XGBoost,Tree-based,693.4907982506384,1.7397156137573087,15.775712987807076,1394.4,201.0,False,0.0
149
- iLTM,Foundation Model,296.63929176158115,68.17329693701195,23.043017100027516,1103.5,1.0,False,0.0
150
- iLTM,Foundation Model,761.0284006743531,133.58534546802647,20.349313351022555,1173.0,2.0,False,0.0
151
- iLTM,Foundation Model,2323.5909795410653,253.2846359590774,18.812213351704102,1278.0,5.0,False,0.0
152
- iLTM,Foundation Model,4948.456215600054,348.32007675170894,17.541943153993884,1341.2,10.0,False,0.0
153
- iLTM,Foundation Model,12120.256080215093,469.70642335709636,15.35397948820616,1419.4,25.0,False,0.0
154
- iLTM,Foundation Model,12685.081550183375,463.2685781743115,15.32495866465839,1423.0,26.0,False,0.0
155
- xRFM,Other,3.2328671519382346,0.9186215875564591,26.46345800176384,1050.2,1.0,False,0.0
156
- xRFM,Other,7.775795304879809,0.871141043689068,23.22748715206326,1112.9,2.0,False,0.0
157
- xRFM,Other,20.620430421907656,1.133551260828972,19.273412112959537,1230.3,5.0,False,0.0
158
- xRFM,Other,41.99625808068535,1.4441868364810944,17.524789542834434,1312.0,10.0,False,0.0
159
- xRFM,Other,105.70497940399669,1.8072467962280723,16.693771119799536,1350.7,25.0,False,0.0
160
- xRFM,Other,210.31944482616692,2.1086840459278653,16.424264842001456,1362.8,50.0,False,0.0
161
- xRFM,Other,422.08708123786937,2.3155613238258024,16.321595045395437,1365.4,100.0,False,0.0
162
- xRFM,Other,631.9086612835005,2.533833287124123,16.300273763804636,1366.1,150.0,False,0.0
163
- xRFM,Other,846.8860702336882,2.566047516252313,16.296898496190295,1365.5,201.0,False,0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/tuning_trajectories_explorer.html DELETED
@@ -1,1230 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena tuning trajectories</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Two-column layout: controls + chips in a side panel, chart beside it.
269
- ``chips-right`` mirrors the columns. Wraps to stacked when narrow. */
270
- .explorer-grid { display: flex; gap: 18px; align-items: flex-start; }
271
- .explorer-grid.chips-right { flex-direction: row-reverse; }
272
- .sidebox { flex: 0 0 330px; min-width: 250px; display: flex; flex-direction: column; gap: 10px; }
273
- .mainbox { flex: 1 1 auto; min-width: 0; }
274
- @media (max-width: 860px) {
275
- .explorer-grid { flex-wrap: wrap; }
276
- .sidebox { flex: 1 1 100%; }
277
- }
278
-
279
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
280
- .chartbox { position: relative; }
281
- /* The chart is sized in device pixels by render() rather than scaled from a
282
- viewBox: scaling stretched the type along with the plot, and a viewBox tall
283
- enough to read made the whole panel own the screen. */
284
- .chartbox svg { display: block; }
285
- /* Chips scroll within the column so the panel height follows the chart. */
286
- .sidebox .chips { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent; }
287
- .sidebox .chips::-webkit-scrollbar { width: 9px; }
288
- .sidebox .chips::-webkit-scrollbar-thumb {
289
- background: var(--pt-muted); border-radius: 8px; border: 3px solid transparent; background-clip: content-box;
290
- }
291
- </style>
292
- </head>
293
- <body>
294
- <div class="viewbar">
295
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
296
- </div>
297
- <p class="explorer-title" id="title"></p>
298
- <div class="explorer-grid" id="grid">
299
- <div class="sidebox">
300
- <div class="controls">
301
- <label class="metricpick" id="metricpick" hidden>Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <div class="btnrow">
305
- <button class="btn" id="btn-front">Pareto front</button>
306
- <button class="btn" id="btn-all">All</button>
307
- <button class="btn" id="btn-none">Clear</button>
308
- </div>
309
- <span class="hint">Click methods or family buttons to highlight &middot; hover points for details</span>
310
- </div>
311
- <div class="chips" id="chips"></div>
312
- </div>
313
- <div class="mainbox">
314
- <div class="exportbar" id="exportbar" hidden>
315
- <span class="hint">Export figure</span>
316
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
317
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
318
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
319
- </div>
320
- <!-- Legend above the chart so readers decode the marks before the data. -->
321
- <div class="legendstrip" id="legendstrip"></div>
322
- <div class="chartbox" id="chartbox">
323
- <svg id="chart" role="img" aria-label="Pareto front explorer"></svg>
324
- <div class="tooltip"></div>
325
- </div>
326
- </div>
327
- </div>
328
- <details class="datatable">
329
- <summary>Data table</summary>
330
- <div class="tblwrap" id="tblwrap"></div>
331
- </details>
332
-
333
- <script>
334
- (function () {
335
- "use strict";
336
- const CONFIG = {"mode": "trajectory", "title": null, "metrics": [{"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": ""}], "xAxes": [{"key": "x_train", "label": "Train time", "axisLabel": "Train time per 1K samples (s), median \u2014 log scale", "short": "Train (s/1K, median)"}], "chipsSide": "right"};
337
- const POINTS = [{"method":"CatBoost","family":"Tree-based","x_train":5.876676442,"imp":15.5457736304,"elo":1392.9,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"CatBoost","family":"Tree-based","x_train":11.6678251256,"imp":15.1182419551,"elo":1413.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"CatBoost","family":"Tree-based","x_train":31.9106517624,"imp":14.848578649,"elo":1430.8,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"CatBoost","family":"Tree-based","x_train":62.5721251757,"imp":14.7477568558,"elo":1435.8,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"CatBoost","family":"Tree-based","x_train":155.9049355669,"imp":14.6406177695,"elo":1441.2,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"CatBoost","family":"Tree-based","x_train":317.345916891,"imp":14.6311446381,"elo":1441.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"CatBoost","family":"Tree-based","x_train":662.4830210734,"imp":14.6478465613,"elo":1440.2,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"CatBoost","family":"Tree-based","x_train":998.8498259224,"imp":14.6673563694,"elo":1438.8,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"CatBoost","family":"Tree-based","x_train":1346.2106911803,"imp":14.674240095,"elo":1437.7,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":1.9886321895,"imp":18.6780631139,"elo":1273.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":4.4383850203,"imp":18.3366328085,"elo":1294.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":10.0701848574,"imp":18.116797306,"elo":1307.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":20.4734178164,"imp":17.9259739747,"elo":1318.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":52.7173757556,"imp":17.6537027938,"elo":1330.8,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":106.3941874905,"imp":17.5022920851,"elo":1338.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":212.8207478205,"imp":17.339314755,"elo":1346.0,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":318.9957179838,"imp":17.2700034244,"elo":1348.8,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":427.391750384,"imp":17.2326457261,"elo":1351.7,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"EBM","family":"Tree-based","x_train":6.6731468509,"imp":20.2759337918,"elo":1208.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"EBM","family":"Tree-based","x_train":16.5765343457,"imp":19.8786837436,"elo":1222.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"EBM","family":"Tree-based","x_train":47.7385589033,"imp":19.4534296615,"elo":1243.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"EBM","family":"Tree-based","x_train":89.4110724699,"imp":19.2726704565,"elo":1252.8,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"EBM","family":"Tree-based","x_train":218.448124708,"imp":19.0438192816,"elo":1263.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"EBM","family":"Tree-based","x_train":440.2545735965,"imp":18.9023411836,"elo":1271.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"EBM","family":"Tree-based","x_train":856.7647171053,"imp":18.780391801,"elo":1278.4,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"EBM","family":"Tree-based","x_train":1266.0910927829,"imp":18.7369843166,"elo":1281.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"EBM","family":"Tree-based","x_train":1711.2458519871,"imp":18.6945455008,"elo":1284.9,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"ExtraTrees","family":"Tree-based","x_train":1.8719729433,"imp":26.1948484129,"elo":1009.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ExtraTrees","family":"Tree-based","x_train":3.1259596548,"imp":24.3488542313,"elo":1053.5,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ExtraTrees","family":"Tree-based","x_train":6.7721912257,"imp":21.6826802288,"elo":1124.8,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ExtraTrees","family":"Tree-based","x_train":13.288092342,"imp":20.760558803,"elo":1165.9,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ExtraTrees","family":"Tree-based","x_train":32.8419339595,"imp":20.1069857393,"elo":1197.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ExtraTrees","family":"Tree-based","x_train":65.4938634865,"imp":19.9445772606,"elo":1207.8,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ExtraTrees","family":"Tree-based","x_train":130.8369715186,"imp":19.8340399339,"elo":1214.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ExtraTrees","family":"Tree-based","x_train":196.2244804971,"imp":19.7910126465,"elo":1218.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ExtraTrees","family":"Tree-based","x_train":263.0379305179,"imp":19.7984513883,"elo":1219.9,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"FastaiMLP","family":"Neural Network","x_train":2.8561491959,"imp":25.7013457941,"elo":1012.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"FastaiMLP","family":"Neural Network","x_train":5.5343795004,"imp":24.5020141267,"elo":1049.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"FastaiMLP","family":"Neural Network","x_train":14.5510344275,"imp":22.6534704395,"elo":1116.6,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"FastaiMLP","family":"Neural Network","x_train":29.723343154,"imp":21.7464416276,"elo":1149.9,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"FastaiMLP","family":"Neural Network","x_train":72.5444980812,"imp":20.8458197285,"elo":1180.3,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"FastaiMLP","family":"Neural Network","x_train":145.754371191,"imp":20.3916263193,"elo":1196.9,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"FastaiMLP","family":"Neural Network","x_train":293.9475008279,"imp":20.0488916705,"elo":1207.2,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"FastaiMLP","family":"Neural Network","x_train":441.8525399102,"imp":19.826636643,"elo":1215.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"FastaiMLP","family":"Neural Network","x_train":593.2377884029,"imp":19.6935269069,"elo":1220.6,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"LightGBM","family":"Tree-based","x_train":1.960032688,"imp":19.3647921745,"elo":1188.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"LightGBM","family":"Tree-based","x_train":4.1465368891,"imp":17.8536668429,"elo":1267.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"LightGBM","family":"Tree-based","x_train":10.3907433568,"imp":16.6855138703,"elo":1346.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"LightGBM","family":"Tree-based","x_train":20.2701675373,"imp":16.0840498218,"elo":1386.9,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"LightGBM","family":"Tree-based","x_train":51.487448334,"imp":15.5280964347,"elo":1421.6,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"LightGBM","family":"Tree-based","x_train":103.6855742013,"imp":15.3158433015,"elo":1437.2,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"LightGBM","family":"Tree-based","x_train":207.5435114784,"imp":15.1552157574,"elo":1447.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"LightGBM","family":"Tree-based","x_train":310.2926532568,"imp":15.0933557354,"elo":1451.2,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"LightGBM","family":"Tree-based","x_train":417.0474735649,"imp":15.0561804683,"elo":1453.8,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"LimiX","family":"Foundation Model","x_train":26.4597512682,"imp":15.4843630349,"elo":1381.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ModernNCA","family":"Neural Network","x_train":14.8696863446,"imp":19.2146348547,"elo":1258.8,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ModernNCA","family":"Neural Network","x_train":40.5171687704,"imp":17.9235444693,"elo":1296.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ModernNCA","family":"Neural Network","x_train":111.4498310885,"imp":16.0052009744,"elo":1381.4,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ModernNCA","family":"Neural Network","x_train":224.929440425,"imp":15.5140651615,"elo":1406.1,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ModernNCA","family":"Neural Network","x_train":554.8880523029,"imp":15.0054551931,"elo":1431.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ModernNCA","family":"Neural Network","x_train":1145.4516650488,"imp":15.0529606984,"elo":1428.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ModernNCA","family":"Neural Network","x_train":2295.4928354707,"imp":15.3121547879,"elo":1419.9,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ModernNCA","family":"Neural Network","x_train":3433.7924746694,"imp":15.5114179492,"elo":1411.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ModernNCA","family":"Neural Network","x_train":4621.6656335635,"imp":15.6253232633,"elo":1406.7,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":27.322832323,"imp":32.2510059823,"elo":936.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":60.3304655332,"imp":30.0030285719,"elo":1001.6,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":157.3958954151,"imp":26.6320054165,"elo":1088.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":185.3067056521,"imp":26.4602162594,"elo":1095.6,"imputed":false,"imputed_pct":0.0,"n_configs":6.0},{"method":"RandomForest","family":"Tree-based","x_train":0.4343250827,"imp":26.2932402488,"elo":999.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RandomForest","family":"Tree-based","x_train":2.3006968127,"imp":23.6312931455,"elo":1058.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RandomForest","family":"Tree-based","x_train":7.8694051654,"imp":22.2945002711,"elo":1101.7,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RandomForest","family":"Tree-based","x_train":17.2473724895,"imp":21.598133622,"elo":1131.3,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RandomForest","family":"Tree-based","x_train":45.0373615697,"imp":21.1082364807,"elo":1162.1,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RandomForest","family":"Tree-based","x_train":91.3917233329,"imp":20.9763766453,"elo":1170.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RandomForest","family":"Tree-based","x_train":184.6996741609,"imp":20.8893896531,"elo":1176.8,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RandomForest","family":"Tree-based","x_train":277.8099565315,"imp":20.8512167326,"elo":1179.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RandomForest","family":"Tree-based","x_train":373.2371055096,"imp":20.8463533192,"elo":1181.1,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"RealMLP","family":"Neural Network","x_train":10.055649238,"imp":18.5902394535,"elo":1235.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RealMLP","family":"Neural Network","x_train":24.6705594408,"imp":16.2369194285,"elo":1345.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RealMLP","family":"Neural Network","x_train":69.643527334,"imp":14.5607377501,"elo":1438.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RealMLP","family":"Neural Network","x_train":141.1529977589,"imp":13.9427929116,"elo":1472.7,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RealMLP","family":"Neural Network","x_train":364.6011469598,"imp":13.4671732129,"elo":1502.2,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RealMLP","family":"Neural Network","x_train":736.9754853516,"imp":13.2471506373,"elo":1515.4,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RealMLP","family":"Neural Network","x_train":1469.2653973047,"imp":13.0647764231,"elo":1524.1,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RealMLP","family":"Neural Network","x_train":2197.5320041687,"imp":12.9722032627,"elo":1529.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RealMLP","family":"Neural Network","x_train":2950.7244476948,"imp":12.9046673494,"elo":1532.8,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":5.8097985299,"imp":11.6839820929,"elo":1538.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":16.6950892358,"imp":11.6143468084,"elo":1544.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":45.8180610371,"imp":11.292487148,"elo":1561.1,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":96.056645332,"imp":11.1111899349,"elo":1578.5,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":245.1410909168,"imp":10.868476471,"elo":1599.1,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":498.5667116885,"imp":10.6868240527,"elo":1608.6,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":1015.1487598759,"imp":10.5680156723,"elo":1613.1,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":1526.7130408425,"imp":10.5076542069,"elo":1614.3,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":2040.2228420973,"imp":10.4555406579,"elo":1613.3,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"SAP-RPT-OSS","family":"Foundation Model","x_train":14.1132668455,"imp":18.2986171534,"elo":1297.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabDPT","family":"Foundation Model","x_train":47.6466878891,"imp":17.2447142424,"elo":1337.6,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabDPT","family":"Foundation Model","x_train":70.407219275,"imp":16.4273759647,"elo":1364.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabDPT","family":"Foundation Model","x_train":132.9785409439,"imp":15.6129784957,"elo":1399.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabDPT","family":"Foundation Model","x_train":243.7764468154,"imp":15.2584743138,"elo":1415.5,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabDPT","family":"Foundation Model","x_train":599.1452639259,"imp":14.5879282112,"elo":1442.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabDPT","family":"Foundation Model","x_train":1224.6320092925,"imp":14.2465622774,"elo":1456.9,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabDPT","family":"Foundation Model","x_train":2440.7040454743,"imp":14.025515153,"elo":1467.0,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabDPT","family":"Foundation Model","x_train":3652.693914187,"imp":13.8836168304,"elo":1472.2,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabDPT","family":"Foundation Model","x_train":4910.3773062373,"imp":13.8120012204,"elo":1474.3,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabDPT-Turbo","family":"Foundation Model","x_train":2.0676840515,"imp":14.0203361292,"elo":1472.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabFM","family":"Foundation Model","x_train":38.8059088678,"imp":4.0944582803,"elo":1807.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabICLv2","family":"Foundation Model","x_train":2.0524528172,"imp":10.1989756415,"elo":1611.8,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabM","family":"Neural Network","x_train":7.4864639685,"imp":17.0218795202,"elo":1307.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabM","family":"Neural Network","x_train":16.6863025317,"imp":16.4957522836,"elo":1334.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabM","family":"Neural Network","x_train":56.5960636953,"imp":15.8572340345,"elo":1370.8,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabM","family":"Neural Network","x_train":118.7995551511,"imp":15.2854538522,"elo":1405.2,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabM","family":"Neural Network","x_train":301.7168598673,"imp":14.7930237659,"elo":1431.2,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabM","family":"Neural Network","x_train":606.795758935,"imp":14.5049392091,"elo":1447.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabM","family":"Neural Network","x_train":1211.551032833,"imp":14.301458535,"elo":1459.4,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabM","family":"Neural Network","x_train":1826.0696865509,"imp":14.1570213529,"elo":1465.8,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabM","family":"Neural Network","x_train":2450.1270774379,"imp":14.1019482991,"elo":1471.1,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabPFN-2.6","family":"Foundation Model","x_train":5.4825798778,"imp":10.7738922898,"elo":1643.6,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabPFN-3","family":"Foundation Model","x_train":3.65846503,"imp":9.3502735666,"elo":1683.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabSTAR","family":"Foundation Model","x_train":384.7456301814,"imp":30.4741238844,"elo":997.8,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabSTAR","family":"Foundation Model","x_train":579.5314694238,"imp":30.347038883,"elo":1006.6,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabSTAR","family":"Foundation Model","x_train":986.9188087142,"imp":30.0809439358,"elo":1014.9,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabSTAR","family":"Foundation Model","x_train":1626.2057458405,"imp":29.4210570456,"elo":1032.7,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabSTAR","family":"Foundation Model","x_train":3523.6508469471,"imp":28.2250520331,"elo":1055.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabSTAR","family":"Foundation Model","x_train":7090.4373429841,"imp":27.0261952401,"elo":1076.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabSTAR","family":"Foundation Model","x_train":14236.6030369928,"imp":26.3553702322,"elo":1092.3,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabSTAR","family":"Foundation Model","x_train":21385.328840253,"imp":25.9429054894,"elo":1103.1,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabSTAR","family":"Foundation Model","x_train":28729.7373655693,"imp":25.6201849214,"elo":1114.2,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabSwift","family":"Foundation Model","x_train":1.1753394519,"imp":16.1020832631,"elo":1367.0,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TorchMLP","family":"Neural Network","x_train":9.9925632138,"imp":22.8295561037,"elo":1082.6,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TorchMLP","family":"Neural Network","x_train":23.3976730308,"imp":21.109494918,"elo":1136.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TorchMLP","family":"Neural Network","x_train":67.83522273,"imp":19.5850220402,"elo":1188.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TorchMLP","family":"Neural Network","x_train":150.5961763502,"imp":18.8591735804,"elo":1222.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TorchMLP","family":"Neural Network","x_train":358.8914252792,"imp":18.1777335918,"elo":1252.0,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TorchMLP","family":"Neural Network","x_train":711.8242682675,"imp":17.7628541744,"elo":1273.5,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TorchMLP","family":"Neural Network","x_train":1402.3570975877,"imp":17.4684976164,"elo":1287.5,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TorchMLP","family":"Neural Network","x_train":2125.0611186926,"imp":17.3117399788,"elo":1296.2,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TorchMLP","family":"Neural Network","x_train":2875.5203407747,"imp":17.1778661155,"elo":1302.7,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"XGBoost","family":"Tree-based","x_train":1.9409389396,"imp":18.8126923396,"elo":1218.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"XGBoost","family":"Tree-based","x_train":5.7736976246,"imp":17.1651628717,"elo":1301.3,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"XGBoost","family":"Tree-based","x_train":17.1658020762,"imp":16.6325079028,"elo":1337.3,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"XGBoost","family":"Tree-based","x_train":34.0813643193,"imp":16.3148977734,"elo":1358.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"XGBoost","family":"Tree-based","x_train":86.4155038646,"imp":16.0688154505,"elo":1372.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"XGBoost","family":"Tree-based","x_train":172.6999358448,"imp":15.965588217,"elo":1379.4,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"XGBoost","family":"Tree-based","x_train":344.2808053206,"imp":15.8832179383,"elo":1385.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"XGBoost","family":"Tree-based","x_train":515.1408947248,"imp":15.8239279243,"elo":1389.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"XGBoost","family":"Tree-based","x_train":693.4907982506,"imp":15.7757129878,"elo":1394.4,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"iLTM","family":"Foundation Model","x_train":296.6392917616,"imp":23.0430171,"elo":1103.5,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"iLTM","family":"Foundation Model","x_train":761.0284006744,"imp":20.349313351,"elo":1173.0,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"iLTM","family":"Foundation Model","x_train":2323.5909795411,"imp":18.8122133517,"elo":1278.0,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"iLTM","family":"Foundation Model","x_train":4948.4562156001,"imp":17.541943154,"elo":1341.2,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"iLTM","family":"Foundation Model","x_train":12120.2560802151,"imp":15.3539794882,"elo":1419.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"iLTM","family":"Foundation Model","x_train":12685.0815501834,"imp":15.3249586647,"elo":1423.0,"imputed":false,"imputed_pct":0.0,"n_configs":26.0},{"method":"xRFM","family":"Other","x_train":3.2328671519,"imp":26.4634580018,"elo":1050.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"xRFM","family":"Other","x_train":7.7757953049,"imp":23.2274871521,"elo":1112.9,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"xRFM","family":"Other","x_train":20.6204304219,"imp":19.273412113,"elo":1230.3,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"xRFM","family":"Other","x_train":41.9962580807,"imp":17.5247895428,"elo":1312.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"xRFM","family":"Other","x_train":105.704979404,"imp":16.6937711198,"elo":1350.7,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"xRFM","family":"Other","x_train":210.3194448262,"imp":16.424264842,"elo":1362.8,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"xRFM","family":"Other","x_train":422.0870812379,"imp":16.3215950454,"elo":1365.4,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"xRFM","family":"Other","x_train":631.9086612835,"imp":16.3002737638,"elo":1366.1,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"xRFM","family":"Other","x_train":846.8860702337,"imp":16.2968984962,"elo":1365.5,"imputed":false,"imputed_pct":0.0,"n_configs":201.0}];
338
-
339
-
340
- const NS = "http://www.w3.org/2000/svg";
341
- // Baseline and Other are one bucket, as in the site's own type legend: they
342
- // already share a color, and each holds only a handful of methods.
343
- const FAM_MERGED = "Baseline / Other";
344
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
345
- // Normalized up front so every later lookup — colors, chips, sorting — sees
346
- // the merged family. Both templates declare POINTS above this block.
347
- for (const p of POINTS) p.family = famOf(p.family);
348
-
349
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
350
- const FAM_VAR = {
351
- "Foundation Model": "var(--fam-foundation)",
352
- "Tree-based": "var(--fam-tree)",
353
- "Neural Network": "var(--fam-nn)",
354
- "Reference Pipeline": "var(--fam-reference)",
355
- [FAM_MERGED]: "var(--fam-baseline)",
356
- };
357
- // The symbol the website shows for each family, so a family chip here reads the
358
- // same as the Type column on the site. Baseline and Other are one bucket, so
359
- // that chip carries both symbols.
360
- const FAM_SYMBOL = {
361
- "Foundation Model": "🧠⚡",
362
- "Tree-based": "🌳",
363
- "Neural Network": "🧠🔁",
364
- "Reference Pipeline": "📊",
365
- [FAM_MERGED]: "📏 ❓",
366
- };
367
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
368
- const VARIANT_VAR = {
369
- "Default": "var(--var-default)",
370
- "Tuned": "var(--var-tuned)",
371
- "Tuned + Ens.": "var(--var-tunedens)",
372
- };
373
-
374
- // A family chip's label: its symbol, its name and how many methods it holds.
375
- function famChipLabel(family, count) {
376
- const symbol = FAM_SYMBOL[family];
377
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
378
- family + ' <span class="count">&times;' + count + "</span>";
379
- }
380
-
381
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
382
- const FAM_INK = {
383
- "Foundation Model": "var(--fam-foundation-ink)",
384
- "Tree-based": "var(--fam-tree-ink)",
385
- "Neural Network": "var(--fam-nn-ink)",
386
- "Reference Pipeline": "var(--fam-reference-ink)",
387
- [FAM_MERGED]: "var(--fam-baseline-ink)",
388
- };
389
-
390
- // Create an SVG element with attributes, optionally appended to `parent`.
391
- function el(name, attrs, parent) {
392
- const node = document.createElementNS(NS, name);
393
- for (const k in attrs) node.setAttribute(k, attrs[k]);
394
- if (parent) parent.appendChild(node);
395
- return node;
396
- }
397
-
398
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
399
- // locale-independent by definition, which is the point: `toLocaleString`
400
- // would follow the *viewer's* browser locale and print 1234,5 for a German
401
- // visitor, disagreeing with the figures and CSVs beside it.
402
- function fmtNum(v, decimals) {
403
- if (v == null || !isFinite(v)) return "—";
404
- return v.toFixed(decimals);
405
- }
406
-
407
- function fmtMetric(metric, v) {
408
- if (v == null || !isFinite(v)) return "—";
409
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
410
- }
411
-
412
- function fmtTime(v) {
413
- if (v >= 100) return fmtNum(v, 0) + " s";
414
- if (v >= 1) return fmtNum(v, 1) + " s";
415
- if (v >= 0.1) return fmtNum(v, 2) + " s";
416
- return fmtNum(v, 3) + " s";
417
- }
418
-
419
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
420
- function niceStep(raw) {
421
- if (!(raw > 0)) return 1;
422
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
423
- for (const m of [1, 2, 2.5, 5, 10]) {
424
- if (mag * m >= raw) return mag * m;
425
- }
426
- return mag * 10;
427
- }
428
-
429
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
430
- // index rather than by accumulation so fractional steps do not drift.
431
- function ticks(min, max, target) {
432
- const step = niceStep((max - min) / target);
433
- const first = Math.ceil(min / step);
434
- const out = [];
435
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
436
- return out.length ? out : [min];
437
- }
438
-
439
- // A hover tooltip anchored inside `box` (which must be position:relative).
440
- function makeTooltip(box) {
441
- const node = box.querySelector(".tooltip");
442
- return {
443
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
444
- move(ev) {
445
- const r = box.getBoundingClientRect();
446
- let tx = ev.clientX - r.left + 14;
447
- const ty = ev.clientY - r.top + 12;
448
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
449
- node.style.left = tx + "px";
450
- node.style.top = ty + "px";
451
- },
452
- hide() { node.style.display = "none"; },
453
- };
454
- }
455
-
456
- // Paper view — white surface, legend + chart only — is the *default*: what a
457
- // reader wants first is the figure, and it is the state worth exporting. The
458
- // controls, chip list and data table are one click away behind "Edit view".
459
- // `afterToggle` re-renders charts whose size is measured from the layout.
460
- // `options.openInPaper` (default true) decides the state the page opens in. A
461
- // chart opens as the figure; the leaderboard table opens with its controls,
462
- // since there the interaction is the point rather than scaffolding around it.
463
- function setUpPaperView(afterToggle, options) {
464
- const opts = options || {};
465
- const root = document.documentElement;
466
- let hostTheme = null; // the embedding page's choice, captured on entry
467
- const btn = document.getElementById("btn-paper");
468
- const embedded = window.parent !== window;
469
-
470
- function setPaper(on) {
471
- document.body.classList.toggle("paper", on);
472
- if (on) {
473
- hostTheme = root.getAttribute("data-theme");
474
- root.setAttribute("data-theme", "light");
475
- } else if (hostTheme) {
476
- root.setAttribute("data-theme", hostTheme);
477
- } else {
478
- root.removeAttribute("data-theme");
479
- }
480
- btn.textContent = on ? "Edit view" : "Paper view";
481
- document.getElementById("exportbar").hidden = !on || embedded;
482
- if (afterToggle) requestAnimationFrame(afterToggle);
483
- postHeight();
484
- }
485
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
486
- // Embedded, the host page owns these controls — they sit beside the panel's
487
- // static-figure toggle and are driven from the outside. Standalone (the
488
- // shareable single file) this page needs its own.
489
- if (embedded) document.querySelector(".viewbar").hidden = true;
490
- window.addEventListener("message", ev => {
491
- const d = ev.data;
492
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
493
- });
494
- // Only standalone: embedded, the host owns the button and would not see the
495
- // key press, so its label would fall out of step with the frame.
496
- if (!embedded) {
497
- document.addEventListener("keydown", ev => {
498
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
499
- });
500
- }
501
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
502
- }
503
-
504
- // --- Figure export ---------------------------------------------------------
505
- // The chart is live SVG, so a file can be built from it directly. Three things
506
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
507
- // nothing outside this document), it has no background or font of its own, and
508
- // the legend is HTML rather than part of the SVG.
509
-
510
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
511
- // cloned and its label re-emitted at the measured position. foreignObject would
512
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
513
- // break the PNG path.
514
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
515
- // once the node leaves this document.
516
- function resolveVars(root, resolve) {
517
- for (const node of [root, ...root.querySelectorAll("*")]) {
518
- for (const attr of ["fill", "stroke"]) {
519
- const value = node.getAttribute(attr);
520
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
521
- }
522
- }
523
- }
524
-
525
- function legendToSvg(container, resolve) {
526
- const base = container.getBoundingClientRect();
527
- const group = document.createElementNS(NS, "g");
528
- let height = 0;
529
- for (const item of container.querySelectorAll(".item")) {
530
- const box = item.getBoundingClientRect();
531
- if (!box.width) continue;
532
- height = Math.max(height, box.bottom - base.top);
533
- let textLeft = box.left - base.left;
534
- const glyph = item.querySelector("svg");
535
- if (glyph) {
536
- const gbox = glyph.getBoundingClientRect();
537
- const wrap = el("g", {
538
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
539
- }, group);
540
- const glyphClone = glyph.cloneNode(true);
541
- resolveVars(glyphClone, resolve);
542
- wrap.appendChild(glyphClone);
543
- textLeft = gbox.right - base.left + 5;
544
- }
545
- const label = item.textContent.trim();
546
- if (!label) continue;
547
- const colored = item.querySelector("[style*='color']");
548
- const text = el("text", {
549
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
550
- fill: resolve(getComputedStyle(colored || item).color),
551
- }, group);
552
- text.textContent = label;
553
- }
554
- return { group, height: Math.ceil(height) };
555
- }
556
-
557
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
558
- // y-axis beside the scrolling plot) still exports as one figure.
559
- function buildExportSvg(parts, legendEl, pad = 10) {
560
- const rootStyle = getComputedStyle(document.documentElement);
561
- const resolve = value => String(value).replace(
562
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
563
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
564
-
565
- let chartW = 0, chartH = 0;
566
- for (const part of parts) {
567
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
568
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
569
- }
570
-
571
- const out = document.createElementNS(NS, "svg");
572
- out.setAttribute("xmlns", NS);
573
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
574
- let top = pad;
575
- const later = []; // built after the width is known
576
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
577
- if (legend && legend.height) {
578
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
579
- later.push(() => out.appendChild(legend.group));
580
- top += legend.height + 8;
581
- }
582
-
583
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
584
- const height = top + chartH + pad;
585
- out.setAttribute("width", Math.ceil(width));
586
- out.setAttribute("height", Math.ceil(height));
587
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
588
- for (const build of later) build();
589
-
590
- for (const part of parts) {
591
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
592
- const clone = part.svg.cloneNode(true);
593
- resolveVars(clone, resolve);
594
- while (clone.firstChild) group.appendChild(clone.firstChild);
595
- }
596
- return out;
597
- }
598
-
599
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
600
- function slugify(text) {
601
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
602
- }
603
-
604
- function downloadUrl(url, filename) {
605
- const a = document.createElement("a");
606
- a.href = url;
607
- a.download = filename;
608
- document.body.appendChild(a);
609
- a.click();
610
- a.remove();
611
- }
612
-
613
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
614
- function rasterize(svg, scale, done, fail) {
615
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
616
- const img = new Image();
617
- img.onload = () => {
618
- const canvas = document.createElement("canvas");
619
- canvas.width = Math.round(width * scale);
620
- canvas.height = Math.round(height * scale);
621
- const ctx = canvas.getContext("2d");
622
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
623
- ctx.drawImage(img, 0, 0);
624
- done(canvas, width, height);
625
- };
626
- img.onerror = fail;
627
- img.src = "data:image/svg+xml;charset=utf-8,"
628
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
629
- }
630
-
631
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
632
- // cost this page its zero-dependency, single-file property. The image is stored
633
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
634
- // in points to the figure's CSS size, so it prints at the size it appears here
635
- // and the pixels land at 96*scale dpi.
636
- async function buildPdf(canvas, cssWidth, cssHeight) {
637
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
638
- const rgb = new Uint8Array((pixels.length / 4) * 3);
639
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
640
- rgb[j] = pixels[i];
641
- rgb[j + 1] = pixels[i + 1];
642
- rgb[j + 2] = pixels[i + 2];
643
- }
644
- const deflated = new Uint8Array(await new Response(
645
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
646
-
647
- const encoder = new TextEncoder();
648
- const chunks = [];
649
- const offsets = [];
650
- let cursor = 0;
651
- const put = data => {
652
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
653
- chunks.push(bytes);
654
- cursor += bytes.length;
655
- };
656
- const object = (id, body, stream) => {
657
- offsets[id] = cursor;
658
- put(`${id} 0 obj\n${body}\n`);
659
- if (stream) {
660
- put("stream\n");
661
- put(stream);
662
- put("\nendstream\n");
663
- }
664
- put("endobj\n");
665
- };
666
-
667
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
668
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
669
- put("%PDF-1.4\n");
670
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
671
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
672
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
673
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
674
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
675
- object(4, "<< /Type /XObject /Subtype /Image "
676
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
677
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
678
- object(5, `<< /Length ${content.length} >>`, content);
679
-
680
- const xref = cursor;
681
- let table = "xref\n0 6\n0000000000 65535 f \n";
682
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
683
- put(table);
684
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
685
- return new Blob(chunks, { type: "application/pdf" });
686
- }
687
-
688
- // Wire up the export buttons; `getParts` is called per click so the file always
689
- // matches what is on screen. Returns a `run(format)` the host page can drive.
690
- function setUpExport(getParts, basename) {
691
- const buttons = {
692
- svg: document.getElementById("btn-svg"),
693
- png: document.getElementById("btn-png"),
694
- pdf: document.getElementById("btn-pdf"),
695
- };
696
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
697
-
698
- // A sandboxed frame has no modals, so a failure is reported on the button.
699
- function complain(format) {
700
- const button = buttons[format];
701
- if (!button) return;
702
- const label = button.textContent;
703
- button.textContent = "failed";
704
- setTimeout(() => { button.textContent = label; }, 2500);
705
- }
706
-
707
- function run(format) {
708
- const svg = figure();
709
- const name = basename();
710
- if (format === "svg") {
711
- downloadUrl("data:image/svg+xml;charset=utf-8,"
712
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
713
- return;
714
- }
715
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
716
- // points so the pixels already land near 200 dpi at print size.
717
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
718
- if (format === "png") {
719
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
720
- } else {
721
- buildPdf(canvas, cssWidth, cssHeight)
722
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
723
- .catch(() => complain("pdf"));
724
- }
725
- }, () => complain(format));
726
- }
727
-
728
- for (const format of Object.keys(buttons)) {
729
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
730
- }
731
- // Embedded, the buttons live in the host's panel header (see main.taExport).
732
- window.addEventListener("message", ev => {
733
- const d = ev.data;
734
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
735
- });
736
- }
737
-
738
- // When embedded, report the content height so the host page can size the
739
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
740
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
741
- // clamped to at least the iframe's current viewport, which turns the
742
- // resize round-trip into a grow-forever feedback loop. The change guard
743
- // stops re-posting once the height settles.
744
- let lastPostedHeight = 0;
745
- function postHeight() {
746
- if (window.parent === window) return;
747
- const height = Math.ceil(document.body.offsetHeight);
748
- if (Math.abs(height - lastPostedHeight) < 3) return;
749
- lastPostedHeight = height;
750
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
751
- }
752
-
753
-
754
- const TRAJECTORY = CONFIG.mode === "trajectory";
755
-
756
- const titleEl = document.getElementById("title");
757
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
758
-
759
- // Column order: chips/controls left of the chart, or mirrored.
760
- document.getElementById("grid").classList.add(CONFIG.chipsSide === "right" ? "chips-right" : "chips-left");
761
-
762
- const svg = document.getElementById("chart");
763
- const box = document.getElementById("chartbox");
764
- const chipsBox = document.getElementById("chips");
765
- const controlsBox = document.querySelector(".controls");
766
- const tip = makeTooltip(box);
767
-
768
- // Marker glyph per variant at (cx, cy); trajectories use circles everywhere.
769
- function drawMark(parent, cx, cy, variant, color, size, opacity, dataM, whiteStroke) {
770
- const common = { opacity: opacity, "data-m": dataM };
771
- let node;
772
- if (TRAJECTORY || variant === "Default" || !variant) {
773
- node = el("circle", { ...common, cx, cy, r: size, fill: color }, parent);
774
- } else if (variant === "Tuned") {
775
- const s = size * 1.75;
776
- node = el("rect", { ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1.5, fill: color }, parent);
777
- } else if (variant === "Tuned + Ens.") {
778
- const d = size * 0.95;
779
- node = el("path", {
780
- ...common,
781
- d: `M${cx - d},${cy - d} L${cx + d},${cy + d} M${cx - d},${cy + d} L${cx + d},${cy - d}`,
782
- stroke: color, "stroke-width": size * 0.62, fill: "none", "stroke-linecap": "round",
783
- }, parent);
784
- } else {
785
- // Any other variant (e.g. "Baseline", holdout types): diamond.
786
- const s = size * 1.45;
787
- node = el("rect", {
788
- ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1,
789
- fill: color, transform: `rotate(45 ${cx} ${cy})`,
790
- }, parent);
791
- }
792
- if (whiteStroke && variant !== "Tuned + Ens.") {
793
- node.setAttribute("stroke", "var(--card)");
794
- node.setAttribute("stroke-width", "1");
795
- }
796
- return node;
797
- }
798
-
799
- function drawImputedRing(parent, cx, cy, size, color, opacity, dataM) {
800
- el("circle", {
801
- cx, cy, r: size + 4.5, fill: "none", stroke: color, "stroke-width": 1.4,
802
- "stroke-dasharray": "3 2.5", opacity: opacity, "data-m": dataM,
803
- }, parent);
804
- }
805
-
806
- // Fat green arrow pointing into the optimal corner (mirrors the static
807
- // figures' "Optimal" arrow so both read the same way).
808
- function drawOptimalArrow(parent, lowerBetter, M, W, H) {
809
- const cornerY = lowerBetter ? H - M.b - 12 : M.t + 12;
810
- const tailY = lowerBetter ? H - M.b - 64 : M.t + 64;
811
- const cx = M.l + 12, tx = M.l + 64;
812
- const dx = cx - tx, dy = cornerY - tailY;
813
- const len = Math.hypot(dx, dy);
814
- const ux = dx / len, uy = dy / len;
815
- const headLen = 16;
816
- const bx = cx - ux * headLen, by = cornerY - uy * headLen; // head base center
817
- // line stops at the head base
818
- el("line", {
819
- x1: tx, y1: tailY, x2: bx, y2: by,
820
- stroke: "var(--optimal)", "stroke-width": 13, "stroke-linecap": "round", opacity: 0.92,
821
- }, parent);
822
- const px = -uy, py = ux; // perpendicular
823
- el("polygon", {
824
- points: `${cx},${cornerY} ${bx + px * 11},${by + py * 11} ${bx - px * 11},${by - py * 11}`,
825
- fill: "var(--optimal)", opacity: 0.92,
826
- }, parent);
827
- let angle = Math.atan2(dy, dx) * 180 / Math.PI;
828
- if (angle > 90 || angle < -90) angle += 180;
829
- const mx = (tx + bx) / 2, my = (tailY + by) / 2;
830
- const t = el("text", {
831
- x: mx, y: my, "text-anchor": "middle", "dominant-baseline": "middle",
832
- "font-size": 10.5, "font-weight": 700, fill: "#ffffff",
833
- transform: `rotate(${angle} ${mx} ${my})`,
834
- }, parent);
835
- t.textContent = "Optimal";
836
- }
837
-
838
- // ---------- data ----------
839
- const byMethod = new Map();
840
- for (const p of POINTS) {
841
- if (!byMethod.has(p.method)) byMethod.set(p.method, []);
842
- byMethod.get(p.method).push(p); // insertion order = builder's point order
843
- }
844
-
845
- const METRICS = CONFIG.metrics;
846
- let metricKey = METRICS[0].key;
847
- const metricByKey = {};
848
- for (const m of METRICS) metricByKey[m.key] = m;
849
-
850
- // Single time axis per explorer (the scatter ships inference time, the
851
- // trajectories train time).
852
- const X_AXIS = CONFIG.xAxes[0];
853
- const xKey = X_AXIS.key;
854
-
855
- function mval(p, metric) { return p[metric.key]; }
856
-
857
- function computeFront(metric) {
858
- const xk = xKey;
859
- const pts = [...POINTS].sort((a, b) =>
860
- a[xk] - b[xk] || (metric.lowerBetter ? mval(a, metric) - mval(b, metric) : mval(b, metric) - mval(a, metric)));
861
- const verts = [];
862
- const methods = new Set();
863
- let best = null;
864
- for (const p of pts) {
865
- const v = mval(p, metric);
866
- if (best === null || (metric.lowerBetter ? v < best : v > best)) {
867
- if (best !== null) verts.push([p[xk], best]);
868
- verts.push([p[xk], v]);
869
- best = v;
870
- methods.add(p.method);
871
- }
872
- }
873
- return { verts, methods };
874
- }
875
-
876
- const state = { active: new Set(computeFront(metricByKey[metricKey]).methods) };
877
-
878
- // ---------- chart ----------
879
- // A flat, fixed-height plot: two of these panels then fit on one screen.
880
- const CHART_H = 400;
881
- const M = { l: 62, r: 18, t: 14, b: 52 };
882
-
883
- function render() {
884
- const metric = metricByKey[metricKey];
885
- svg.textContent = "";
886
- const W = Math.max(360, Math.round(box.clientWidth));
887
- const H = CHART_H;
888
- svg.setAttribute("width", W);
889
- svg.setAttribute("height", H);
890
- // Keep the chip list from outgrowing the chart beside it.
891
- chipsBox.style.maxHeight = Math.max(170, H - controlsBox.offsetHeight + 20) + "px";
892
-
893
- // x scale (log)
894
- const xsAll = POINTS.map(p => p[xKey]);
895
- const xmin = Math.min(...xsAll) * 0.65, xmax = Math.max(...xsAll) * 1.6;
896
- const lx0 = Math.log10(xmin), lx1 = Math.log10(xmax);
897
- const X = v => M.l + (Math.log10(v) - lx0) / (lx1 - lx0) * (W - M.l - M.r);
898
-
899
- const vals = POINTS.map(p => mval(p, metric));
900
- let y0, y1;
901
- if (metric.fromZero) {
902
- y0 = 0; y1 = Math.max(...vals) * 1.07;
903
- } else {
904
- const pad = (Math.max(...vals) - Math.min(...vals)) * 0.07;
905
- y0 = Math.min(...vals) - pad; y1 = Math.max(...vals) + pad;
906
- }
907
- const Y = v => M.t + (1 - (v - y0) / (y1 - y0)) * (H - M.t - M.b);
908
-
909
- // grid + axes
910
- const grid = el("g", {}, svg);
911
- for (let e = Math.ceil(lx0); Math.pow(10, e) < xmax; e++) {
912
- const gx = X(Math.pow(10, e));
913
- el("line", { x1: gx, y1: M.t, x2: gx, y2: H - M.b, stroke: "var(--line)", "stroke-width": 1 }, grid);
914
- const lbl = fmtNum(Math.pow(10, e), e >= 0 ? 0 : -e);
915
- el("text", { x: gx, y: H - M.b + 20, "text-anchor": "middle", "font-size": 12.5, fill: "var(--muted)" }, grid)
916
- .textContent = lbl;
917
- }
918
- for (const yv of ticks(y0, y1, 6)) {
919
- const gy = Y(yv);
920
- el("line", { x1: M.l, y1: gy, x2: W - M.r, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
921
- el("text", { x: M.l - 8, y: gy + 4, "text-anchor": "end", "font-size": 12.5, fill: "var(--muted)" }, grid)
922
- .textContent = fmtNum(yv, Number.isInteger(yv) ? 0 : metric.decimals);
923
- }
924
- el("rect", { x: M.l, y: M.t, width: W - M.l - M.r, height: H - M.t - M.b, fill: "none", stroke: "var(--line)" }, grid);
925
- el("text", {
926
- x: (M.l + W - M.r) / 2, y: H - 10, "text-anchor": "middle", "font-size": 14,
927
- "font-weight": 650, fill: "var(--ink)",
928
- }, grid).textContent = X_AXIS.axisLabel;
929
- el("text", {
930
- x: 0, y: 0, "text-anchor": "middle", "font-size": 14, "font-weight": 650, fill: "var(--ink)",
931
- transform: `translate(16 ${(M.t + H - M.b) / 2}) rotate(-90)`,
932
- }, grid).textContent = metric.axisLabel;
933
-
934
- drawOptimalArrow(grid, metric.lowerBetter, M, W, H);
935
-
936
- // pareto front (always shown)
937
- const front = computeFront(metric);
938
- const fv = front.verts;
939
- if (fv.length) {
940
- let d = `M${X(fv[0][0])},${metric.lowerBetter ? M.t : H - M.b}`;
941
- for (const [fx, fy] of fv) d += ` L${X(fx)},${Y(fy)}`;
942
- d += ` L${W - M.r},${Y(fv[fv.length - 1][1])}`;
943
- el("path", { d, fill: "none", stroke: "var(--ink)", "stroke-width": 1.6, "stroke-dasharray": "7 5", opacity: 0.85 }, svg);
944
- }
945
-
946
- const isOn = m => state.active.has(m);
947
-
948
- // connectors: variant links (scatter) / the trajectory itself
949
- const conn = el("g", {}, svg);
950
- for (const [method, pts] of byMethod) {
951
- if (pts.length < 2) continue;
952
- const on = isOn(method);
953
- if (!TRAJECTORY && !on) continue; // scatter: connectors only for active methods
954
- const dd = pts.map((p, i) => `${i ? "L" : "M"}${X(p[xKey])},${Y(mval(p, metric))}`).join(" ");
955
- el("path", {
956
- d: dd, fill: "none",
957
- stroke: on ? FAM_VAR[pts[0].family] : "var(--pt-muted)",
958
- "stroke-width": on ? (TRAJECTORY ? 2 : 1.4) : 1,
959
- opacity: on ? 0.6 : 0.35,
960
- "data-m": method,
961
- }, conn);
962
- }
963
-
964
- // points: inactive first, active on top
965
- const ptsOff = el("g", {}, svg);
966
- const ptsOn = el("g", {}, svg);
967
- for (const [method, pts] of byMethod) {
968
- const on = isOn(method);
969
- for (const p of pts) {
970
- const color = on ? FAM_VAR[p.family] : "var(--pt-muted)";
971
- const size = (on ? 7 : 5) * (TRAJECTORY ? 0.8 : 1);
972
- const op = on ? 0.95 : 0.5;
973
- drawMark(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), p.variant, color, size, op, p.method, on);
974
- // Imputation ring: every affected point in scatter mode; only the
975
- // trajectory's end point in trajectory mode (a ring on all ~8 line
976
- // points would read as beads, and the chip's ‡ already flags the line).
977
- if (p.imputed && (!TRAJECTORY || p === pts[pts.length - 1])) {
978
- drawImputedRing(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), size, color, op, p.method);
979
- }
980
- }
981
- }
982
-
983
- // labels for active methods at their best point, greedy de-overlap
984
- const labels = [];
985
- for (const [method, pts] of byMethod) {
986
- if (!isOn(method)) continue;
987
- const best = pts.reduce((a, b) =>
988
- (metric.lowerBetter ? mval(a, metric) < mval(b, metric) : mval(a, metric) > mval(b, metric)) ? a : b);
989
- labels.push({ method, family: best.family, x: X(best[xKey]) + 10, y: Y(mval(best, metric)) - 10 });
990
- }
991
- labels.sort((a, b) => a.y - b.y);
992
- for (let i = 1; i < labels.length; i++) {
993
- for (let j = 0; j < i; j++) {
994
- if (Math.abs(labels[i].x - labels[j].x) < 110 && Math.abs(labels[i].y - labels[j].y) < 15) {
995
- labels[i].y = labels[j].y + 15;
996
- }
997
- }
998
- }
999
- const lg = el("g", {}, svg);
1000
- for (const l of labels) {
1001
- const t = el("text", {
1002
- x: Math.min(l.x, W - M.r - 8), y: Math.max(l.y, M.t + 12), "font-size": 13, "font-weight": 700,
1003
- fill: FAM_VAR[l.family], "paint-order": "stroke", stroke: "var(--card)", "stroke-width": 3.5,
1004
- "text-anchor": l.x > W - 120 ? "end" : "start",
1005
- }, lg);
1006
- t.textContent = l.method;
1007
- }
1008
-
1009
- // invisible hit targets on top (bigger than marks)
1010
- const hits = el("g", {}, svg);
1011
- for (const p of POINTS) {
1012
- const h = el("circle", { cx: X(p[xKey]), cy: Y(mval(p, metric)), r: 12, fill: "transparent", cursor: "pointer" }, hits);
1013
- h.addEventListener("mouseenter", ev => showTip(p, ev));
1014
- h.addEventListener("mousemove", ev => tip.move(ev));
1015
- h.addEventListener("mouseleave", () => hideTip(p.method));
1016
- h.addEventListener("click", () => toggle(p.method));
1017
- }
1018
- }
1019
-
1020
- // Temporary hover emphasis without a re-render (a re-render would replace
1021
- // the hit node under the cursor mid-hover).
1022
- function emphasize(method, on) {
1023
- svg.querySelectorAll(`[data-m="${CSS.escape(method)}"]`).forEach(n => {
1024
- if (on) {
1025
- if (!n.dataset.save) n.dataset.save = n.getAttribute("opacity") || "1";
1026
- n.setAttribute("opacity", "0.95");
1027
- } else if (n.dataset.save) {
1028
- n.setAttribute("opacity", n.dataset.save);
1029
- delete n.dataset.save;
1030
- }
1031
- });
1032
- }
1033
-
1034
- function showTip(p, ev) {
1035
- emphasize(p.method, true);
1036
- const sub = TRAJECTORY ? (p.n_configs != null ? `${p.n_configs} configs` : "") : (p.variant || "");
1037
- let html = `<div class="t-name">${p.method}` + (sub ? ` <span class="t-var">(${sub})</span>` : "") + "</div>" +
1038
- `<div>${p.family}</div>`;
1039
- for (const m of METRICS) {
1040
- html += `<div>${m.label}: <b>${fmtMetric(m, mval(p, m))}</b></div>`;
1041
- }
1042
- html += `<div>${X_AXIS.short}: <b>${fmtTime(p[xKey])}</b></div>`;
1043
- if (p.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(p.imputed_pct, 0)}% of datasets</div>`;
1044
- tip.show(html, ev);
1045
- }
1046
- function hideTip(method) {
1047
- emphasize(method, false);
1048
- tip.hide();
1049
- }
1050
-
1051
- // ---------- chips ----------
1052
- const chipByMethod = new Map();
1053
- const famChips = new Map();
1054
- function familyMethods(fam) {
1055
- return [...byMethod.keys()].filter(m => byMethod.get(m)[0].family === fam);
1056
- }
1057
- // Chips are listed by leaderboard rank — best Elo first when Elo is
1058
- // configured, otherwise best value of the primary metric.
1059
- const RANK_METRIC = metricByKey["elo"] || metricByKey[METRICS[0].key];
1060
- function bestVal(method) {
1061
- const vals = byMethod.get(method).map(p => mval(p, RANK_METRIC));
1062
- return RANK_METRIC.lowerBetter ? Math.min(...vals) : Math.max(...vals);
1063
- }
1064
- function rankSorted(methods) {
1065
- return [...methods].sort((a, b) =>
1066
- RANK_METRIC.lowerBetter ? bestVal(a) - bestVal(b) : bestVal(b) - bestVal(a));
1067
- }
1068
- function buildChips() {
1069
- for (const fam of FAM_ORDER) {
1070
- const methods = rankSorted(familyMethods(fam));
1071
- if (!methods.length) continue;
1072
- const row = document.createElement("div");
1073
- row.className = "chiprow";
1074
- const famBtn = document.createElement("button");
1075
- famBtn.className = "famchip";
1076
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1077
- famBtn.innerHTML = famChipLabel(fam, methods.length);
1078
- famBtn.title = `Toggle all ${methods.length} ${fam} methods`;
1079
- famBtn.addEventListener("click", () => toggleFamily(fam));
1080
- row.appendChild(famBtn);
1081
- famChips.set(fam, famBtn);
1082
- const set = document.createElement("div");
1083
- set.className = "chipset";
1084
- for (const m of methods) {
1085
- const b = document.createElement("button");
1086
- b.className = "chip";
1087
- b.style.setProperty("--fam", FAM_VAR[fam]);
1088
- const imputed = byMethod.get(m).some(p => p.imputed);
1089
- const label = document.createElement("span");
1090
- label.textContent = m;
1091
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1092
- b.appendChild(label);
1093
- if (imputed) {
1094
- const mark = document.createElement("span");
1095
- mark.className = "imp-mark";
1096
- mark.textContent = "‡";
1097
- b.appendChild(mark);
1098
- }
1099
- b.title = m + (imputed ? " — partially imputed" : "");
1100
- b.addEventListener("click", () => toggle(m));
1101
- set.appendChild(b);
1102
- chipByMethod.set(m, b);
1103
- }
1104
- row.appendChild(set);
1105
- chipsBox.appendChild(row);
1106
- }
1107
- }
1108
- function syncChips() {
1109
- for (const [m, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.active.has(m)));
1110
- for (const [fam, b] of famChips) {
1111
- b.setAttribute("aria-pressed", String(familyMethods(fam).every(m => state.active.has(m))));
1112
- }
1113
- }
1114
- function toggle(m) {
1115
- if (state.active.has(m)) state.active.delete(m); else state.active.add(m);
1116
- syncChips();
1117
- render();
1118
- }
1119
- function toggleFamily(fam) {
1120
- const methods = familyMethods(fam);
1121
- const allOn = methods.every(m => state.active.has(m));
1122
- for (const m of methods) {
1123
- if (allOn) state.active.delete(m); else state.active.add(m);
1124
- }
1125
- syncChips();
1126
- render();
1127
- }
1128
- function setActive(methods) {
1129
- state.active = new Set(methods);
1130
- syncChips();
1131
- render();
1132
- }
1133
- document.getElementById("btn-front").addEventListener("click",
1134
- () => setActive(computeFront(metricByKey[metricKey]).methods));
1135
- document.getElementById("btn-all").addEventListener("click", () => setActive([...byMethod.keys()]));
1136
- document.getElementById("btn-none").addEventListener("click", () => setActive([]));
1137
-
1138
- // metric selector (hidden when only one metric is configured)
1139
- const metricPick = document.getElementById("metricpick");
1140
- const metricSelect = document.getElementById("metric-select");
1141
- if (METRICS.length > 1) {
1142
- metricPick.hidden = false;
1143
- for (const m of METRICS) {
1144
- const opt = document.createElement("option");
1145
- opt.value = m.key;
1146
- opt.textContent = m.label;
1147
- metricSelect.appendChild(opt);
1148
- }
1149
- metricSelect.addEventListener("change", ev => {
1150
- metricKey = ev.target.value;
1151
- render();
1152
- });
1153
- }
1154
-
1155
- // ---------- legend strip ----------
1156
- function buildLegend() {
1157
- const box2 = document.getElementById("legendstrip");
1158
- let html = "";
1159
- if (!TRAJECTORY) {
1160
- html +=
1161
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><circle cx="7" cy="7" r="5" fill="var(--muted)"/></svg> Default</span>' +
1162
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" rx="1.5" fill="var(--muted)"/></svg> Tuned</span>' +
1163
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><path d="M3,3 L11,11 M3,11 L11,3" stroke="var(--muted)" stroke-width="2.6" stroke-linecap="round"/></svg> Tuned + Ensembled</span>';
1164
- } else {
1165
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--muted)" stroke-width="2"/><circle cx="6" cy="4" r="2.6" fill="var(--muted)"/><circle cx="16" cy="4" r="2.6" fill="var(--muted)"/></svg> Tuning trajectory (more configs &rarr; more time)</span>';
1166
- }
1167
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--ink)" stroke-width="1.6" stroke-dasharray="6 4"/></svg> Pareto front (always shown)</span>';
1168
- if (POINTS.some(p => p.imputed)) {
1169
- html += '<span class="item"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="4" fill="var(--muted)"/><circle cx="9" cy="9" r="7.5" fill="none" stroke="var(--muted)" stroke-width="1.3" stroke-dasharray="3 2.5"/></svg> &Dagger; partially imputed</span>';
1170
- }
1171
- // Model family, named: highlighted points are colored by family, and in paper
1172
- // view the chip list that would otherwise decode them is hidden.
1173
- const families = FAM_ORDER.filter(f => POINTS.some(p => p.family === f));
1174
- if (families.length > 1) {
1175
- html += '<span class="legendbreak"></span><span class="item">Family:</span>';
1176
- for (const fam of families) {
1177
- html += `<span class="item"><svg width="12" height="12" viewBox="0 0 12 12">` +
1178
- `<circle cx="6" cy="6" r="5" fill="${FAM_VAR[fam]}"/></svg> ` +
1179
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`;
1180
- }
1181
- }
1182
- box2.innerHTML = html;
1183
- }
1184
-
1185
- // ---------- paper view ----------
1186
- setUpPaperView(render);
1187
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(document.title));
1188
-
1189
- // ---------- data table ----------
1190
- function buildTable() {
1191
- const m0 = metricByKey[METRICS[0].key];
1192
- const rows = [...POINTS].sort((a, b) =>
1193
- m0.lowerBetter ? mval(a, m0) - mval(b, m0) : mval(b, m0) - mval(a, m0));
1194
- let html = "<table><thead><tr><th>Method</th>";
1195
- html += TRAJECTORY ? "<th>Configs</th>" : "<th>Variant</th>";
1196
- html += "<th>Family</th>";
1197
- for (const m of METRICS) html += `<th>${m.label}</th>`;
1198
- html += `<th>${X_AXIS.short}</th><th>Imputed</th></tr></thead><tbody>`;
1199
- for (const p of rows) {
1200
- html += `<tr><td>${p.method}</td><td>${TRAJECTORY ? (p.n_configs != null ? p.n_configs : "—") : p.variant}</td><td>${p.family}</td>`;
1201
- for (const m of METRICS) html += `<td>${fmtMetric(m, mval(p, m))}</td>`;
1202
- html += `<td>${fmtNum(p[xKey], 3)}</td>`;
1203
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1204
- }
1205
- html += "</tbody></table>";
1206
- document.getElementById("tblwrap").innerHTML = html;
1207
- }
1208
-
1209
- const _renderInner = render;
1210
- render = function () {
1211
- _renderInner();
1212
- postHeight();
1213
- };
1214
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1215
- let resizeTimer = null;
1216
- window.addEventListener("resize", () => {
1217
- clearTimeout(resizeTimer);
1218
- resizeTimer = setTimeout(render, 120);
1219
- });
1220
- window.addEventListener("load", postHeight);
1221
-
1222
- buildChips();
1223
- buildLegend();
1224
- buildTable();
1225
- syncChips();
1226
- render();
1227
- })();
1228
- </script>
1229
- </body>
1230
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/website_leaderboard.csv DELETED
@@ -1,71 +0,0 @@
1
- #,Type,TypeName,Model,Elo [⬆️],Elo 95% CI,Score [⬆️],Rank [⬇️],Harmonic Rank [⬇️],Improvability (%) [⬇️],Median Train Time (s/1K) [⬇️],Median Predict Time (s/1K) [⬇️],Verified,Imputed (%) [⬇️],Imputed,Hardware
2
- 0,🧠⚡,Foundation Model,[TabFM (default)](https://github.com/google-research/tabfm),1792.0,+115/-110,0.889,6.17,1.72,4.074,38.81,6.985,✔️,0.0,False,GPU
3
- 1,📊,Reference Pipeline,"[AutoGluon 1.5 (extreme, 4h)](https://arxiv.org/abs/2003.06505)",1691.0,+82/-68,0.662,9.39,4.03,7.979,289.07,4.031,✔️,0.0,False,GPU
4
- 2,🧠⚡,Foundation Model,[TabPFN-3 (default)](https://arxiv.org/abs/2605.13986),1665.0,+80/-61,0.67,10.41,4.06,9.328,3.66,0.399,✔️,0.0,False,GPU
5
- 3,🧠⚡,Foundation Model,[TabPFN-2.6 (default)](https://arxiv.org/abs/2511.08667),1620.0,+71/-51,0.583,12.42,7.14,10.751,5.48,0.555,✔️,0.0,False,GPU
6
- 4,🧠⚡,Foundation Model,[RealTabPFN-2.5 (tuned + ensembled)](https://arxiv.org/abs/2511.08667),1595.0,+74/-56,0.549,13.64,6.22,10.427,2040.22,8.908,✔️,0.0,False,GPU
7
- 5,🧠⚡,Foundation Model,[TabICLv2 (default)](https://arxiv.org/abs/2602.11139),1594.0,+73/-61,0.606,13.71,4.97,10.175,2.05,0.151,✔️,0.0,False,GPU
8
- 6,🧠⚡,Foundation Model,[RealTabPFN-2.5 (tuned)](https://arxiv.org/abs/2511.08667),1554.0,+65/-55,0.504,15.81,7.33,11.116,2040.22,1.218,✔️,0.0,False,GPU
9
- 7,🧠⚡,Foundation Model,[RealTabPFN-2.5 (default)](https://arxiv.org/abs/2511.08667),1524.0,+62/-44,0.474,17.55,9.62,11.66,5.81,0.645,✔️,0.0,False,GPU
10
- 8,📊,Reference Pipeline,"[AutoGluon 1.4 (best, 4h)](https://arxiv.org/abs/2003.06505)",1514.0,+53/-46,0.401,18.2,8.44,12.789,1735.72,2.558,✔️,0.0,False,CPU
11
- 9,🧠🔁,Neural Network,[RealMLP (tuned + ensembled)](https://arxiv.org/abs/2407.04491),1508.0,+54/-46,0.405,18.55,11.52,12.881,2950.72,11.975,✔️,0.0,False,GPU
12
- 10,🧠⚡,Foundation Model,[TabDPT-Turbo (default)](https://openreview.net/pdf?id=Y00pwFyrHR),1459.0,+55/-51,0.38,21.7,9.59,14.0,2.07,0.182,➖,0.0,False,GPU
13
- 11,🧠⚡,Foundation Model,[TabDPT (tuned + ensembled)](https://arxiv.org/abs/2410.18164),1458.0,+60/-51,0.375,21.81,8.16,13.788,4910.38,286.537,✔️,0.0,False,GPU
14
- 12,🧠🔁,Neural Network,[TabM (tuned + ensembled)](https://arxiv.org/abs/2410.24210),1450.0,+52/-43,0.315,22.33,12.48,14.081,2450.13,2.247,✔️,0.0,False,GPU
15
- 13,🌳,Tree-based,[LightGBM (tuned + ensembled)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1433.0,+33/-31,0.262,23.53,17.2,15.033,417.05,2.639,✔️,0.0,False,CPU
16
- 14,🧠🔁,Neural Network,[RealMLP (tuned)](https://arxiv.org/abs/2407.04491),1426.0,+48/-45,0.295,23.99,13.67,14.202,2950.72,0.663,✔️,0.0,False,GPU
17
- 15,🌳,Tree-based,[CatBoost (tuned + ensembled)](https://arxiv.org/abs/1706.09516),1422.0,+39/-38,0.261,24.34,14.77,14.656,1346.21,0.344,✔️,0.0,False,CPU
18
- 16,🌳,Tree-based,[CatBoost (tuned)](https://arxiv.org/abs/1706.09516),1408.0,+38/-43,0.245,25.3,15.31,14.885,1346.21,0.038,✔️,0.0,False,CPU
19
- 17,🧠⚡,Foundation Model,[iLTM (tuned + ensembled)](https://arxiv.org/abs/2511.15941),1404.0,+40/-42,0.235,25.56,17.78,15.304,12685.08,464.37,✔️,0.0,False,GPU
20
- 18,🧠⚡,Foundation Model,[TabDPT (tuned)](https://arxiv.org/abs/2410.18164),1403.0,+59/-56,0.316,25.69,11.3,14.936,4910.38,39.96,✔️,0.0,False,GPU
21
- 19,🧠🔁,Neural Network,[TabM (tuned)](https://arxiv.org/abs/2410.24210),1394.0,+48/-42,0.251,26.35,14.47,14.952,2450.13,0.251,✔️,0.0,False,GPU
22
- 20,🧠🔁,Neural Network,[ModernNCA (tuned + ensembled)](https://arxiv.org/abs/2407.03257),1389.0,+73/-52,0.277,26.66,12.79,15.599,4621.67,8.138,✔️,0.0,False,GPU
23
- 21,🌳,Tree-based,[LightGBM (tuned)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1386.0,+29/-29,0.194,26.93,20.71,15.688,417.05,0.334,✔️,0.0,False,CPU
24
- 22,🌳,Tree-based,[CatBoost (default)](https://arxiv.org/abs/1706.09516),1378.0,+40/-42,0.224,27.47,16.11,15.524,5.88,0.025,✔️,0.0,False,CPU
25
- 23,🌳,Tree-based,[XGBoost (tuned + ensembled)](https://arxiv.org/abs/1603.02754),1375.0,+31/-33,0.198,27.73,19.98,15.754,693.49,1.689,✔️,0.0,False,CPU
26
- 24,🧠⚡,Foundation Model,[LimiX (default)](https://arxiv.org/abs/2509.03505),1363.0,+78/-59,0.299,28.66,9.55,15.526,26.46,6.242,➖,0.0,False,GPU
27
- 25,🧠🔁,Neural Network,[ModernNCA (tuned)](https://arxiv.org/abs/2407.03257),1355.0,+37/-37,0.189,29.21,16.82,16.044,4621.67,0.475,✔️,0.0,False,GPU
28
- 26,🧠⚡,Foundation Model,[TabSwift (default)](https://github.com/LAMDA-Tabular/TabSwift),1350.0,+58/-52,0.251,29.58,17.68,16.081,1.18,0.072,➖,0.0,False,GPU
29
- 27,🌳,Tree-based,[XGBoost (tuned)](https://arxiv.org/abs/1603.02754),1350.0,+29/-32,0.16,29.6,22.22,16.065,693.49,0.308,✔️,0.0,False,CPU
30
- 28,❓,Other,[xRFM (tuned + ensembled)](https://arxiv.org/abs/2508.10053),1348.0,+49/-43,0.209,29.79,18.96,16.284,846.89,2.549,✔️,0.0,False,GPU
31
- 29,🌳,Tree-based,[ChimeraBoost (tuned + ensembled)](https://github.com/bbstats/chimeraboost),1333.0,+46/-60,0.186,30.87,19.34,17.213,427.39,0.404,✔���,0.0,False,CPU
32
- 30,🧠⚡,Foundation Model,[TabDPT (default)](https://arxiv.org/abs/2410.18164),1325.0,+64/-55,0.245,31.53,14.33,17.225,47.65,43.738,✔️,0.0,False,GPU
33
- 31,🌳,Tree-based,[ChimeraBoost (tuned)](https://github.com/bbstats/chimeraboost),1308.0,+44/-60,0.156,32.79,21.32,17.551,427.39,0.034,✔️,0.0,False,CPU
34
- 32,❓,Other,[xRFM (tuned)](https://arxiv.org/abs/2508.10053),1300.0,+45/-39,0.158,33.43,22.28,17.449,846.89,0.13,✔️,0.0,False,GPU
35
- 33,🧠🔁,Neural Network,[TabM (default)](https://arxiv.org/abs/2410.24210),1298.0,+47/-44,0.161,33.63,21.84,17.0,7.49,0.244,✔️,0.0,False,GPU
36
- 34,🧠⚡,Foundation Model,[iLTM (tuned)](https://arxiv.org/abs/2511.15941),1295.0,+40/-33,0.111,33.83,23.47,17.147,12685.08,62.13,✔️,0.0,False,GPU
37
- 35,🧠🔁,Neural Network,[TorchMLP (tuned + ensembled)](https://arxiv.org/abs/2003.06505),1285.0,+48/-47,0.126,34.58,25.37,17.161,2875.52,1.952,✔️,0.0,False,CPU
38
- 36,🧠⚡,Foundation Model,[SAP-RPT-OSS (default)](https://arxiv.org/abs/2506.10707),1283.0,+57/-55,0.194,34.72,14.11,18.278,14.11,2.075,➖,0.0,False,GPU
39
- 37,🌳,Tree-based,[EBM (tuned + ensembled)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1272.0,+40/-40,0.116,35.64,25.8,18.672,1711.25,0.141,✔️,0.0,False,CPU
40
- 38,🌳,Tree-based,[ChimeraBoost (default)](https://github.com/bbstats/chimeraboost),1264.0,+47/-55,0.121,36.23,23.23,18.657,1.99,0.033,✔️,0.0,False,CPU
41
- 39,🧠🔁,Neural Network,[ModernNCA (default)](https://arxiv.org/abs/2407.03257),1248.0,+41/-39,0.09,37.44,23.68,19.194,14.87,0.307,✔️,0.0,False,GPU
42
- 40,🌳,Tree-based,[EBM (tuned)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1235.0,+44/-40,0.082,38.43,28.17,19.314,1711.25,0.015,✔️,0.0,False,CPU
43
- 41,🧠🔁,Neural Network,[RealMLP (default)](https://arxiv.org/abs/2407.04491),1230.0,+36/-37,0.066,38.85,29.23,18.569,10.06,1.689,✔️,0.0,False,GPU
44
- 42,🌳,Tree-based,[XGBoost (default)](https://arxiv.org/abs/1603.02754),1214.0,+39/-36,0.053,40.04,30.76,18.792,1.94,0.123,✔️,0.0,False,CPU
45
- 43,🧠🔁,Neural Network,[TorchMLP (tuned)](https://arxiv.org/abs/2003.06505),1212.0,+41/-42,0.069,40.24,30.96,18.877,2875.52,0.131,✔️,0.0,False,CPU
46
- 44,🌳,Tree-based,[ExtraTrees (tuned + ensembled)](https://link.springer.com/article/10.1007/s10994-006-6226-1),1208.0,+49/-43,0.083,40.47,28.16,19.777,263.04,0.766,✔️,0.0,False,CPU
47
- 45,🧠🔁,Neural Network,[FastaiMLP (tuned + ensembled)](https://arxiv.org/abs/2003.06505),1207.0,+51/-59,0.105,40.58,25.38,19.677,593.24,4.467,✔️,0.0,False,CPU
48
- 46,🌳,Tree-based,[EBM (default)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1204.0,+45/-49,0.069,40.82,28.54,20.255,6.67,0.014,✔️,0.0,False,CPU
49
- 47,🌳,Tree-based,[LightGBM (default)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1186.0,+34/-29,0.038,42.12,36.15,19.344,1.96,0.142,✔️,0.0,False,CPU
50
- 48,🌳,Tree-based,[ExtraTrees (tuned)](https://link.springer.com/article/10.1007/s10994-006-6226-1),1180.0,+45/-52,0.074,42.56,26.73,20.564,263.04,0.071,✔️,0.0,False,CPU
51
- 49,🌳,Tree-based,[RandomForest (tuned + ensembled)](https://link.springer.com/article/10.1023/A:1010933404324),1171.0,+54/-47,0.06,43.28,29.04,20.828,373.24,0.771,✔️,0.0,False,CPU
52
- 50,🧠🔁,Neural Network,[FastaiMLP (tuned)](https://arxiv.org/abs/2003.06505),1147.0,+52/-59,0.048,45.01,31.19,21.096,593.24,0.306,✔️,0.0,False,CPU
53
- 51,🌳,Tree-based,[RandomForest (tuned)](https://link.springer.com/article/10.1023/A:1010933404324),1137.0,+50/-44,0.041,45.71,28.56,21.535,373.24,0.085,✔️,0.0,False,CPU
54
- 52,🧠⚡,Foundation Model,[TabSTAR (tuned)](https://arxiv.org/abs/2505.18125),1097.0,+75/-83,0.125,48.47,15.95,25.584,28729.74,4.268,✔️,0.0,False,GPU
55
- 53,🧠⚡,Foundation Model,[TabSTAR (tuned + ensembled)](https://arxiv.org/abs/2505.18125),1096.0,+79/-83,0.123,48.56,15.35,25.6,28729.74,18.811,✔️,0.0,False,GPU
56
- 54,🧠⚡,Foundation Model,[iLTM (default)](https://arxiv.org/abs/2511.15941),1094.0,+47/-54,0.037,48.66,33.93,23.024,296.64,68.173,✔️,0.0,False,GPU
57
- 55,🌳,Tree-based,[PerpetualBooster (tuned + ensembled)](https://perpetual-ml.com/),1089.0,+44/-45,0.024,49.04,38.83,26.446,185.31,0.6,✔️,0.0,False,CPU
58
- 56,🧠🔁,Neural Network,[TorchMLP (default)](https://arxiv.org/abs/2003.06505),1074.0,+34/-42,0.011,49.99,39.42,22.815,9.99,0.126,✔️,0.0,False,CPU
59
- 57,🌳,Tree-based,[PerpetualBooster (tuned)](https://perpetual-ml.com/),1050.0,+47/-50,0.011,51.52,39.96,27.802,185.31,0.258,✔️,0.0,False,CPU
60
- 58,❓,Other,[xRFM (default)](https://arxiv.org/abs/2508.10053),1043.0,+61/-70,0.029,51.95,44.11,26.445,3.23,0.919,✔️,0.0,False,GPU
61
- 59,🌳,Tree-based,[ExtraTrees (default)](https://link.springer.com/article/10.1007/s10994-006-6226-1),1009.0,+51/-66,0.012,54.0,47.38,26.176,1.87,0.283,✔️,0.0,False,CPU
62
- 60,🧠🔁,Neural Network,[FastaiMLP (default)](https://arxiv.org/abs/2003.06505),1008.0,+57/-62,0.009,54.07,39.97,25.682,2.86,0.373,✔️,0.0,False,CPU
63
- 61,🌳,Tree-based,[RandomForest (default)](https://link.springer.com/article/10.1023/A:1010933404324),1000.0,+47/-47,0.003,54.51,49.81,26.265,0.43,0.054,✔️,0.0,False,CPU
64
- 62,📏,Baseline,[KNN (tuned + ensembled)](https://scikit-learn.org/stable/modules/neighbors.html),997.0,+61/-76,0.017,54.67,38.92,27.822,129.08,1.801,✔️,0.0,False,CPU
65
- 63,🧠⚡,Foundation Model,[TabSTAR (default)](https://arxiv.org/abs/2505.18125),993.0,+89/-104,0.077,54.89,24.31,30.456,384.75,5.348,✔️,0.0,False,GPU
66
- 64,📏,Baseline,[Linear (tuned + ensembled)](https://scikit-learn.org/stable/modules/linear_model.html),965.0,+64/-100,0.031,56.39,40.87,33.667,237.63,0.424,✔️,0.0,False,CPU
67
- 65,📏,Baseline,[Linear (tuned)](https://scikit-learn.org/stable/modules/linear_model.html),940.0,+70/-105,0.022,57.67,42.64,34.211,237.63,0.085,✔️,0.0,False,CPU
68
- 66,🌳,Tree-based,[PerpetualBooster (default)](https://perpetual-ml.com/),936.0,+43/-57,0.0,57.84,45.44,31.748,27.32,0.029,✔️,0.0,False,CPU
69
- 67,📏,Baseline,[KNN (tuned)](https://scikit-learn.org/stable/modules/neighbors.html),890.0,+61/-100,0.008,59.92,46.93,32.782,129.08,0.18,✔️,0.0,False,CPU
70
- 68,📏,Baseline,[Linear (default)](https://scikit-learn.org/stable/modules/linear_model.html),861.0,+69/-118,0.014,61.12,47.51,36.822,1.19,0.12,✔️,0.0,False,CPU
71
- 69,📏,Baseline,[KNN (default)](https://scikit-learn.org/stable/modules/neighbors.html),654.0,+78/-91,0.0,66.89,60.42,45.987,0.19,0.036,✔️,0.0,False,CPU
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/winrate_explorer.html DELETED
@@ -1,1169 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Arena win-rate matrix</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* The matrix scrolls in both directions: 40+ models will not fit a page either
269
- way, and shrinking the cells to fit makes the numbers unreadable. `margin:
270
- auto` centres it once the selection is small enough to fit — and collapses
271
- to zero when it overflows, which `justify-content: center` would not. */
272
- .wr-scroll {
273
- overflow: auto; max-width: 100%;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .wr-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
277
- .wr-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- #chart { display: block; margin-inline: auto; }
282
- .wr-empty { padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
283
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
284
- .chips { margin: 2px 0 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
285
- .chiprow { flex: 0 1 auto; min-width: 0; }
286
- </style>
287
- </head>
288
- <body>
289
- <div class="viewbar">
290
- <button class="btn" id="btn-paper" title="White background, matrix only — for slides and papers">Paper view</button>
291
- </div>
292
- <p class="explorer-title" id="title"></p>
293
- <div class="controls">
294
- <button class="btn toggle" id="btn-best"
295
- title="Keep only each model's best-performing variant, one row and column per model — as the static figure does">One per model</button>
296
- <span class="grouplabel">Variants</span>
297
- <div class="btnrow" id="variant-btns"></div>
298
- <label class="metricpick">Order
299
- <select id="order-select">
300
- <option value="published">As published</option>
301
- <option value="winrate">Mean win rate</option>
302
- <option value="name">Name</option>
303
- </select>
304
- </label>
305
- <label class="metricpick">Size
306
- <select id="zoom-select" title="Fit scales the whole matrix into the panel; the percentages draw it at reading size and scroll">
307
- <option value="fit">Fit panel</option>
308
- <option value="0.6">60%</option>
309
- <option value="0.8">80%</option>
310
- <option value="1">100%</option>
311
- </select>
312
- </label>
313
- <span class="hint">Click a row or column label to remove that model</span>
314
- </div>
315
- <div class="exportbar" id="exportbar" hidden>
316
- <span class="hint">Export figure</span>
317
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">Download SVG</button>
318
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">Download PDF</button>
319
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">Download PNG</button>
320
- </div>
321
- <div class="chips" id="chips"></div>
322
- <div class="wr-scroll" id="wrap">
323
- <svg id="chart" role="img" aria-label="Pairwise win-rate matrix"></svg>
324
- </div>
325
- <div class="tooltip"></div>
326
-
327
- <script>
328
- (function () {
329
- "use strict";
330
- const CONFIG = {"title": null, "methods": ["TabFM (default)", "TabPFN-3 (default)", "TabPFN-2.6 (default)", "TabICLv2 (default)", "RealTabPFN-2.5 (T+E)", "RealMLP (T+E)", "TabDPT-Turbo (default)", "TabDPT (T+E)", "TabM (T+E)", "LightGBM (T+E)", "CatBoost (T+E)", "iLTM (T+E)", "ModernNCA (T+E)", "LimiX (default)", "XGBoost (T+E)", "TabSwift (default)", "xRFM (T+E)", "ChimeraBoost (T+E)", "SAP-RPT-OSS (default)", "TorchMLP (T+E)", "EBM (T+E)", "ExtraTrees (T+E)", "FastaiMLP (T+E)", "RandomForest (T+E)", "TabSTAR (T+E)", "PerpetualBooster (T+E)", "KNN (T+E)", "Linear (T+E)"], "matrix": [[NaN, 0.7877995371818542, 0.832461953163147, 0.8446625471115112, 0.8332244157791138, 0.8671024441719055, 0.9039215445518494, 0.8777777552604675, 0.9117648005485535, 0.9076253771781921, 0.9030501246452332, 0.90152508020401, 0.911328911781311, 0.9027233719825745, 0.9257081747055054, 0.899564266204834, 0.9193899631500244, 0.9211329817771912, 0.9093682765960693, 0.946405291557312, 0.9333334565162659, 0.9507625102996826, 0.9226579666137695, 0.9607843160629272, 0.9008715152740479, 0.9897603988647461, 0.9607843160629272, 0.9747276902198792], [0.21220043301582336, NaN, 0.6313725709915161, 0.5980392098426819, 0.6503269076347351, 0.7633987069129944, 0.8095861673355103, 0.7507625818252563, 0.8098039627075195, 0.8141611814498901, 0.817211389541626, 0.840522825717926, 0.8544661402702332, 0.7888888120651245, 0.8476034998893738, 0.9067538976669312, 0.8559913635253906, 0.8660130500793457, 0.8488017916679382, 0.9095861315727234, 0.872984766960144, 0.9424837231636047, 0.9046840071678162, 0.9368192553520203, 0.9028322696685791, 0.9474945068359375, 0.9496732354164124, 0.9533769488334656], [0.1675381362438202, 0.3686274588108063, NaN, 0.4673202335834503, 0.4984748661518097, 0.7224401831626892, 0.6877995133399963, 0.688235342502594, 0.7675382494926453, 0.7921568751335144, 0.80305016040802, 0.813507616519928, 0.8211329579353333, 0.7742919325828552, 0.8326797485351562, 0.8226578831672668, 0.8357298374176025, 0.8614379167556763, 0.8477123975753784, 0.8941175937652588, 0.8880175352096558, 0.9265795946121216, 0.886274516582489, 0.9328976273536682, 0.8921568393707275, 0.9627451300621033, 0.9542483687400818, 0.9525054693222046], [0.15533769130706787, 0.4019608199596405, 0.5326797366142273, NaN, 0.5496732592582703, 0.6989107131958008, 0.7287582159042358, 0.736710250377655, 0.7566449642181396, 0.7668846249580383, 0.7697169184684753, 0.7821350693702698, 0.789760410785675, 0.7700434923171997, 0.7860567569732666, 0.8444445133209229, 0.7936820387840271, 0.7938998341560364, 0.8159041404724121, 0.8474946022033691, 0.8276689052581787, 0.8920480608940125, 0.8697168827056885, 0.8991285562515259, 0.8808279037475586, 0.930065393447876, 0.9529411196708679, 0.9564269781112671], [0.16677561402320862, 0.34967318177223206, 0.5015250444412231, 0.4503267705440521, NaN, 0.6901960968971252, 0.6529412269592285, 0.6607843041419983, 0.7368191480636597, 0.7690632343292236, 0.7671024799346924, 0.7611110806465149, 0.7455337643623352, 0.7605664134025574, 0.8139433860778809, 0.8045751452445984, 0.8111111521720886, 0.806427001953125, 0.8238562941551208, 0.8773420453071594, 0.864052414894104, 0.8827887177467346, 0.8763617873191833, 0.9089323878288269, 0.886274516582489, 0.9679738283157349, 0.9322439432144165, 0.9346405267715454], [0.13289760053157806, 0.236601322889328, 0.27755990624427795, 0.3010893762111664, 0.30980387330055237, NaN, 0.5185185074806213, 0.5011982917785645, 0.5873637795448303, 0.6832243800163269, 0.6779956221580505, 0.7211328148841858, 0.6420479416847229, 0.6246187686920166, 0.7673202753067017, 0.6348583698272705, 0.7873638272285461, 0.7009803056716919, 0.7440088391304016, 0.8043574094772339, 0.802178680896759, 0.8533769249916077, 0.897276759147644, 0.8949890732765198, 0.8551198244094849, 0.9485838413238525, 0.9296296238899231, 0.943572998046875], [0.09607843309640884, 0.1904139369726181, 0.3122004270553589, 0.27124181389808655, 0.34705883264541626, 0.4814814031124115, NaN, 0.49716776609420776, 0.5273420214653015, 0.5732026100158691, 0.6126362085342407, 0.6322440505027771, 0.583442211151123, 0.6368191242218018, 0.6366013288497925, 0.722113311290741, 0.6642701625823975, 0.6688452959060669, 0.6799564957618713, 0.7461873292922974, 0.6816993355751038, 0.8277778029441833, 0.800000011920929, 0.845969557762146, 0.8178650140762329, 0.889760434627533, 0.9220044016838074, 0.8710241317749023], [0.12222223728895187, 0.24923749268054962, 0.31176474690437317, 0.26328974962234497, 0.3392156958580017, 0.49880173802375793, 0.502832293510437, NaN, 0.5465141534805298, 0.5873638391494751, 0.586928129196167, 0.6372548937797546, 0.5880174040794373, 0.5922658443450928, 0.6138344407081604, 0.6667755246162415, 0.6900871396064758, 0.6533769369125366, 0.6984749436378479, 0.7306101322174072, 0.7038127183914185, 0.8111111521720886, 0.7825707793235779, 0.8453159928321838, 0.8008715510368347, 0.9117648005485535, 0.9208061099052429, 0.8586056232452393], [0.0882352963089943, 0.19019603729248047, 0.2324618697166443, 0.2433551400899887, 0.26318079233169556, 0.4126361310482025, 0.4726580083370209, 0.4534858167171478, NaN, 0.5718954801559448, 0.5877995491027832, 0.5844226479530334, 0.5686275362968445, 0.5265794396400452, 0.6440086960792542, 0.6037037372589111, 0.6080610752105713, 0.6732025742530823, 0.6956428289413452, 0.7422658801078796, 0.7629631161689758, 0.7758169770240784, 0.8163398504257202, 0.7871459722518921, 0.8529411554336548, 0.8919390439987183, 0.9067538976669312, 0.9376905560493469], [0.09237473458051682, 0.18583877384662628, 0.2078431397676468, 0.23311546444892883, 0.23093684017658234, 0.3167755901813507, 0.42679741978645325, 0.4126362204551697, 0.42810454964637756, NaN, 0.5143791437149048, 0.525381326675415, 0.5034857988357544, 0.4766884744167328, 0.7058824896812439, 0.5838780403137207, 0.5912854671478271, 0.6857298016548157, 0.6505447030067444, 0.6858387589454651, 0.7537038326263428, 0.7979304194450378, 0.766884446144104, 0.8446623086929321, 0.8398692607879639, 0.9313726425170898, 0.9183006286621094, 0.9305011630058289], [0.09694989770650864, 0.1827886551618576, 0.19694988429546356, 0.2302832007408142, 0.23289760947227478, 0.3220043480396271, 0.38736382126808167, 0.4130719006061554, 0.4122004806995392, 0.4856208860874176, NaN, 0.5322440266609192, 0.5337690114974976, 0.48104575276374817, 0.6332244277000427, 0.5793028473854065, 0.5962962508201599, 0.6686274409294128, 0.6758170127868652, 0.6973856091499329, 0.729411780834198, 0.7967320084571838, 0.7627449631690979, 0.8492375612258911, 0.8387799263000488, 0.9074073433876038, 0.9143790602684021, 0.9172112941741943], [0.09847494214773178, 0.1594771146774292, 0.18649239838123322, 0.21786490082740784, 0.23888882994651794, 0.27886706590652466, 0.3677560091018677, 0.36274513602256775, 0.41557735204696655, 0.47461873292922974, 0.4677559435367584, NaN, 0.4877995252609253, 0.45762529969215393, 0.5728757977485657, 0.545969545841217, 0.5859478116035461, 0.5999999642372131, 0.6542484164237976, 0.7239652276039124, 0.6869280934333801, 0.783115565776825, 0.8023965954780579, 0.8392158150672913, 0.809259295463562, 0.876470685005188, 0.9041393995285034, 0.8884533047676086], [0.08867102116346359, 0.14553378522396088, 0.1788671314716339, 0.21023967862129211, 0.2544662356376648, 0.35795214772224426, 0.4165577292442322, 0.41198259592056274, 0.4313725233078003, 0.496514230966568, 0.46623098850250244, 0.5122004747390747, NaN, 0.5206971764564514, 0.574292004108429, 0.5703703761100769, 0.569934606552124, 0.5884531140327454, 0.6220043897628784, 0.6592592597007751, 0.6727669835090637, 0.7470586895942688, 0.7244008183479309, 0.775381326675415, 0.7694989442825317, 0.8492375016212463, 0.8700435757637024, 0.837690532207489], [0.09727669507265091, 0.21111111342906952, 0.22570808231830597, 0.22995641827583313, 0.23943357169628143, 0.375381201505661, 0.3631807863712311, 0.4077341556549072, 0.47342050075531006, 0.5233115553855896, 0.5189542770385742, 0.5423746705055237, 0.4793028235435486, NaN, 0.554684042930603, 0.5067538619041443, 0.5180827975273132, 0.6217865347862244, 0.6102396845817566, 0.6187363266944885, 0.674727737903595, 0.6623092889785767, 0.6911765336990356, 0.6956426501274109, 0.7529411911964417, 0.7442266345024109, 0.8154684901237488, 0.8671024441719055], [0.07429194450378418, 0.15239651501178741, 0.16732025146484375, 0.21394334733486176, 0.18605665862560272, 0.23267973959445953, 0.3633987307548523, 0.386165589094162, 0.35599133372306824, 0.29411762952804565, 0.36677560210227966, 0.4271242022514343, 0.42570802569389343, 0.4453159272670746, NaN, 0.5084967017173767, 0.549128532409668, 0.5625271797180176, 0.6069716811180115, 0.6589323878288269, 0.683442234992981, 0.755337655544281, 0.7252722978591919, 0.8282135725021362, 0.8185186386108398, 0.8570805788040161, 0.8982569575309753, 0.914161205291748], [0.10043573379516602, 0.09324619919061661, 0.17734204232692719, 0.15555556118488312, 0.19542483985424042, 0.3651416301727295, 0.2778867185115814, 0.33322441577911377, 0.39629629254341125, 0.4161219596862793, 0.4206971228122711, 0.45403045415878296, 0.4296295940876007, 0.4932461977005005, 0.4915032386779785, NaN, 0.5167755484580994, 0.5145969986915588, 0.6108932495117188, 0.614814817905426, 0.6034857630729675, 0.6795207858085632, 0.7614378929138184, 0.6956426501274109, 0.812853991985321, 0.8383443355560303, 0.8732026219367981, 0.8507625460624695], [0.08061002194881439, 0.14400872588157654, 0.16427013278007507, 0.20631809532642365, 0.18888889253139496, 0.21263618767261505, 0.33572983741760254, 0.30991286039352417, 0.3919389247894287, 0.4087145924568176, 0.4037036895751953, 0.41405224800109863, 0.4300653338432312, 0.48191726207733154, 0.4508714973926544, 0.48322442173957825, NaN, 0.5030500888824463, 0.5579520463943481, 0.587145984172821, 0.6163398623466492, 0.7108932733535767, 0.69248366355896, 0.7629628777503967, 0.7592591643333435, 0.8089324831962585, 0.9225490093231201, 0.9095860123634338], [0.078867107629776, 0.1339869201183319, 0.13856208324432373, 0.20610018074512482, 0.193572998046875, 0.29901957511901855, 0.33115464448928833, 0.34662309288978577, 0.32679736614227295, 0.31427016854286194, 0.33137258887290955, 0.4000000059604645, 0.41154685616493225, 0.378213495016098, 0.43747276067733765, 0.48540303111076355, 0.4969498813152313, NaN, 0.5867102146148682, 0.5897603631019592, 0.6431373357772827, 0.6633987426757812, 0.6562091708183289, 0.727450966835022, 0.801307201385498, 0.8145970106124878, 0.8328975439071655, 0.8795207142829895], [0.09063181281089783, 0.15119825303554535, 0.1522875726222992, 0.18409587442874908, 0.1761437952518463, 0.2559913098812103, 0.32004356384277344, 0.3015250861644745, 0.3043573200702667, 0.349455326795578, 0.32418304681777954, 0.34575164318084717, 0.3779956102371216, 0.389760285615921, 0.39302828907966614, 0.38910675048828125, 0.44204798340797424, 0.41328975558280945, NaN, 0.5030500888824463, 0.5071895718574524, 0.5803921818733215, 0.6381263732910156, 0.5990196466445923, 0.6921569108963013, 0.7361655831336975, 0.8058823943138123, 0.8745097517967224], [0.05359477549791336, 0.09041394293308258, 0.10588236153125763, 0.15250545740127563, 0.12265795469284058, 0.19564269483089447, 0.25381267070770264, 0.26938995718955994, 0.2577342092990875, 0.31416118144989014, 0.30261439085006714, 0.2760348320007324, 0.34074074029922485, 0.3812636435031891, 0.34106752276420593, 0.3851851522922516, 0.41285398602485657, 0.41023963689804077, 0.4969499111175537, NaN, 0.5, 0.6605664491653442, 0.6821351647377014, 0.7006535530090332, 0.7874729037284851, 0.7250545024871826, 0.841394305229187, 0.8798475861549377], [0.06666666269302368, 0.12701524794101715, 0.11198257654905319, 0.17233113944530487, 0.13594771921634674, 0.19782133400440216, 0.31830066442489624, 0.29618731141090393, 0.2370370477437973, 0.24629630148410797, 0.2705882489681244, 0.3130718469619751, 0.32723310589790344, 0.3252723217010498, 0.31655776500701904, 0.3965142071247101, 0.38366013765335083, 0.35686275362968445, 0.49281045794487, 0.5, NaN, 0.6171023845672607, 0.5759259462356567, 0.6315903663635254, 0.7851852178573608, 0.7061002254486084, 0.8313726782798767, 0.9047930240631104], [0.04923747479915619, 0.057516347616910934, 0.07342047989368439, 0.10795208811759949, 0.11721134185791016, 0.14662308990955353, 0.17222221195697784, 0.18888890743255615, 0.22418296337127686, 0.2020697295665741, 0.20326797664165497, 0.2168845385313034, 0.25294119119644165, 0.33769068121910095, 0.2446623295545578, 0.32047930359840393, 0.2891067862510681, 0.33660128712654114, 0.41960784792900085, 0.33943355083465576, 0.38289758563041687, NaN, 0.49389976263046265, 0.6332244277000427, 0.6583877801895142, 0.644444465637207, 0.8858387470245361, 0.7785401940345764], [0.07734205573797226, 0.09531590342521667, 0.11372550576925278, 0.13028322160243988, 0.1236383393406868, 0.10272331535816193, 0.20000001788139343, 0.21742920577526093, 0.1836601346731186, 0.23311546444892883, 0.23725493252277374, 0.19760346412658691, 0.27559909224510193, 0.30882349610328674, 0.27472764253616333, 0.23856207728385925, 0.30751633644104004, 0.34379082918167114, 0.361873596906662, 0.31786492466926575, 0.42407405376434326, 0.5061002373695374, NaN, 0.5604574680328369, 0.6694989204406738, 0.6150326728820801, 0.7636166214942932, 0.8605664372444153], [0.03921568766236305, 0.0631808266043663, 0.06710240244865417, 0.10087146610021591, 0.09106754511594772, 0.10501088201999664, 0.15403051674365997, 0.15468411147594452, 0.21285401284694672, 0.15533769130706787, 0.15076254308223724, 0.1607843041419983, 0.22461874783039093, 0.3043573200702667, 0.17178650200366974, 0.30435726046562195, 0.2370370626449585, 0.27254900336265564, 0.4009803831577301, 0.299346387386322, 0.36840957403182983, 0.36677560210227966, 0.4395425021648407, NaN, 0.6549019813537598, 0.613725483417511, 0.8453158736228943, 0.7675381302833557], [0.09912854433059692, 0.09716776013374329, 0.10784313827753067, 0.1191721186041832, 0.11372549086809158, 0.14488017559051514, 0.18213507533073425, 0.19912856817245483, 0.14705882966518402, 0.16013072431087494, 0.16122004389762878, 0.19074073433876038, 0.23050105571746826, 0.24705885350704193, 0.1814814805984497, 0.18714596331119537, 0.24074076116085052, 0.19869278371334076, 0.3078431189060211, 0.21252721548080444, 0.21481479704380035, 0.34161216020584106, 0.33050107955932617, 0.3450980484485626, NaN, 0.5320261716842651, 0.628322422504425, 0.6775598526000977], [0.010239651426672935, 0.05250544473528862, 0.037254903465509415, 0.06993463635444641, 0.03202614188194275, 0.051416121423244476, 0.11023964732885361, 0.08823530375957489, 0.10806101560592651, 0.06862745434045792, 0.09259260445833206, 0.12352942675352097, 0.15076249837875366, 0.2557734549045563, 0.1429194062948227, 0.16165576875209808, 0.19106753170490265, 0.1854030191898346, 0.2638344168663025, 0.27494552731513977, 0.2938997745513916, 0.35555556416511536, 0.3849673271179199, 0.38627445697784424, 0.46797382831573486, NaN, 0.6161219477653503, 0.6856208443641663], [0.03921568766236305, 0.05032680183649063, 0.04575163871049881, 0.0470588244497776, 0.06775599718093872, 0.07037036865949631, 0.07799564301967621, 0.07919389754533768, 0.09324619174003601, 0.08169934153556824, 0.0856209248304367, 0.0958605632185936, 0.1299564391374588, 0.1845315843820572, 0.10174292325973511, 0.1267973929643631, 0.07745098322629929, 0.1671023815870285, 0.19411763548851013, 0.158605694770813, 0.16862745583057404, 0.11416123062372208, 0.23638343811035156, 0.15468408167362213, 0.37167757749557495, 0.38387802243232727, NaN, 0.6224400997161865], [0.02527233213186264, 0.046623099595308304, 0.0474945567548275, 0.043572988361120224, 0.06535948812961578, 0.056427016854286194, 0.1289760321378708, 0.14139433205127716, 0.06230936944484711, 0.06949891895055771, 0.08278867602348328, 0.1115468442440033, 0.16230936348438263, 0.13289758563041687, 0.08583878725767136, 0.1492374688386917, 0.09041394293308258, 0.12047931551933289, 0.125490203499794, 0.12015251070261002, 0.09520696848630905, 0.22145968675613403, 0.13943354785442352, 0.2324618697166443, 0.32244011759757996, 0.31437912583351135, 0.37755993008613586, NaN]], "variants": ["Default", "Tuned", "Tuned + Ens."]};
331
- const POINTS = [{"method":"TabFM (default)","model":"TabFM","variant":"Default","family":"Foundation Model","mean":0.9074155181},{"method":"TabPFN-3 (default)","model":"TabPFN-3","variant":"Default","family":"Foundation Model","mean":0.8142903458},{"method":"TabPFN-2.6 (default)","model":"TabPFN-2.6","variant":"Default","family":"Foundation Model","mean":0.7726619994},{"method":"TabICLv2 (default)","model":"TabICLv2","variant":"Default","family":"Foundation Model","mean":0.7603526612},{"method":"RealTabPFN-2.5 (T+E)","model":"RealTabPFN-2.5 (T+E)","variant":"","family":"Foundation Model","mean":0.7482570929},{"method":"RealMLP (T+E)","model":"RealMLP (T+E)","variant":"","family":"Neural Network","mean":0.6584321829},{"method":"TabDPT-Turbo (default)","model":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","mean":0.6124223501},{"method":"TabDPT (T+E)","model":"TabDPT (T+E)","variant":"","family":"Foundation Model","mean":0.6116194838},{"method":"TabM (T+E)","model":"TabM (T+E)","variant":"","family":"Neural Network","mean":0.5886670039},{"method":"LightGBM (T+E)","model":"LightGBM (T+E)","variant":"","family":"Tree-based","mean":0.5644719052},{"method":"CatBoost (T+E)","model":"CatBoost (T+E)","variant":"","family":"Tree-based","mean":0.558286926},{"method":"iLTM (T+E)","model":"iLTM (T+E)","variant":"","family":"Foundation Model","mean":0.5365529174},{"method":"ModernNCA (T+E)","model":"ModernNCA (T+E)","variant":"","family":"Neural Network","mean":0.5304325126},{"method":"LimiX (default)","model":"LimiX","variant":"Default","family":"Foundation Model","mean":0.5192971878},{"method":"XGBoost (T+E)","model":"XGBoost (T+E)","variant":"","family":"Tree-based","mean":0.49843056},{"method":"TabSwift (default)","model":"TabSwift","variant":"Default","family":"Foundation Model","mean":0.4878560445},{"method":"xRFM (T+E)","model":"xRFM (T+E)","variant":"","family":"Other","mean":0.4791858218},{"method":"ChimeraBoost (T+E)","model":"ChimeraBoost (T+E)","variant":"","family":"Tree-based","mean":0.4631445193},{"method":"SAP-RPT-OSS (default)","model":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","mean":0.4184216956},{"method":"TorchMLP (T+E)","model":"TorchMLP (T+E)","variant":"","family":"Neural Network","mean":0.4051803513},{"method":"EBM (T+E)","model":"EBM (T+E)","variant":"","family":"Tree-based","mean":0.3942306182},{"method":"ExtraTrees (T+E)","model":"ExtraTrees (T+E)","variant":"","family":"Tree-based","mean":0.325082707},{"method":"FastaiMLP (T+E)","model":"FastaiMLP (T+E)","variant":"","family":"Neural Network","mean":0.3237109618},{"method":"RandomForest (T+E)","model":"RandomForest (T+E)","variant":"","family":"Tree-based","mean":0.2861534742},{"method":"TabSTAR (T+E)","model":"TabSTAR (T+E)","variant":"","family":"Foundation Model","mean":0.2443798877},{"method":"PerpetualBooster (T+E)","model":"PerpetualBooster (T+E)","variant":"","family":"Tree-based","mean":0.2096828785},{"method":"KNN (T+E)","model":"KNN (T+E)","variant":"","family":"Baseline","mean":0.1491204749},{"method":"Linear (T+E)","model":"Linear (T+E)","variant":"","family":"Baseline","mean":0.1322601511}];
332
-
333
-
334
- const NS = "http://www.w3.org/2000/svg";
335
- // Baseline and Other are one bucket, as in the site's own type legend: they
336
- // already share a color, and each holds only a handful of methods.
337
- const FAM_MERGED = "Baseline / Other";
338
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
339
- // Normalized up front so every later lookup — colors, chips, sorting — sees
340
- // the merged family. Both templates declare POINTS above this block.
341
- for (const p of POINTS) p.family = famOf(p.family);
342
-
343
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
344
- const FAM_VAR = {
345
- "Foundation Model": "var(--fam-foundation)",
346
- "Tree-based": "var(--fam-tree)",
347
- "Neural Network": "var(--fam-nn)",
348
- "Reference Pipeline": "var(--fam-reference)",
349
- [FAM_MERGED]: "var(--fam-baseline)",
350
- };
351
- // The symbol the website shows for each family, so a family chip here reads the
352
- // same as the Type column on the site. Baseline and Other are one bucket, so
353
- // that chip carries both symbols.
354
- const FAM_SYMBOL = {
355
- "Foundation Model": "🧠⚡",
356
- "Tree-based": "🌳",
357
- "Neural Network": "🧠🔁",
358
- "Reference Pipeline": "📊",
359
- [FAM_MERGED]: "📏 ❓",
360
- };
361
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
362
- const VARIANT_VAR = {
363
- "Default": "var(--var-default)",
364
- "Tuned": "var(--var-tuned)",
365
- "Tuned + Ens.": "var(--var-tunedens)",
366
- };
367
-
368
- // A family chip's label: its symbol, its name and how many methods it holds.
369
- function famChipLabel(family, count) {
370
- const symbol = FAM_SYMBOL[family];
371
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
372
- family + ' <span class="count">&times;' + count + "</span>";
373
- }
374
-
375
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
376
- const FAM_INK = {
377
- "Foundation Model": "var(--fam-foundation-ink)",
378
- "Tree-based": "var(--fam-tree-ink)",
379
- "Neural Network": "var(--fam-nn-ink)",
380
- "Reference Pipeline": "var(--fam-reference-ink)",
381
- [FAM_MERGED]: "var(--fam-baseline-ink)",
382
- };
383
-
384
- // Create an SVG element with attributes, optionally appended to `parent`.
385
- function el(name, attrs, parent) {
386
- const node = document.createElementNS(NS, name);
387
- for (const k in attrs) node.setAttribute(k, attrs[k]);
388
- if (parent) parent.appendChild(node);
389
- return node;
390
- }
391
-
392
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
393
- // locale-independent by definition, which is the point: `toLocaleString`
394
- // would follow the *viewer's* browser locale and print 1234,5 for a German
395
- // visitor, disagreeing with the figures and CSVs beside it.
396
- function fmtNum(v, decimals) {
397
- if (v == null || !isFinite(v)) return "—";
398
- return v.toFixed(decimals);
399
- }
400
-
401
- function fmtMetric(metric, v) {
402
- if (v == null || !isFinite(v)) return "—";
403
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
404
- }
405
-
406
- function fmtTime(v) {
407
- if (v >= 100) return fmtNum(v, 0) + " s";
408
- if (v >= 1) return fmtNum(v, 1) + " s";
409
- if (v >= 0.1) return fmtNum(v, 2) + " s";
410
- return fmtNum(v, 3) + " s";
411
- }
412
-
413
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
414
- function niceStep(raw) {
415
- if (!(raw > 0)) return 1;
416
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
417
- for (const m of [1, 2, 2.5, 5, 10]) {
418
- if (mag * m >= raw) return mag * m;
419
- }
420
- return mag * 10;
421
- }
422
-
423
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
424
- // index rather than by accumulation so fractional steps do not drift.
425
- function ticks(min, max, target) {
426
- const step = niceStep((max - min) / target);
427
- const first = Math.ceil(min / step);
428
- const out = [];
429
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
430
- return out.length ? out : [min];
431
- }
432
-
433
- // A hover tooltip anchored inside `box` (which must be position:relative).
434
- function makeTooltip(box) {
435
- const node = box.querySelector(".tooltip");
436
- return {
437
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
438
- move(ev) {
439
- const r = box.getBoundingClientRect();
440
- let tx = ev.clientX - r.left + 14;
441
- const ty = ev.clientY - r.top + 12;
442
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
443
- node.style.left = tx + "px";
444
- node.style.top = ty + "px";
445
- },
446
- hide() { node.style.display = "none"; },
447
- };
448
- }
449
-
450
- // Paper view — white surface, legend + chart only — is the *default*: what a
451
- // reader wants first is the figure, and it is the state worth exporting. The
452
- // controls, chip list and data table are one click away behind "Edit view".
453
- // `afterToggle` re-renders charts whose size is measured from the layout.
454
- // `options.openInPaper` (default true) decides the state the page opens in. A
455
- // chart opens as the figure; the leaderboard table opens with its controls,
456
- // since there the interaction is the point rather than scaffolding around it.
457
- function setUpPaperView(afterToggle, options) {
458
- const opts = options || {};
459
- const root = document.documentElement;
460
- let hostTheme = null; // the embedding page's choice, captured on entry
461
- const btn = document.getElementById("btn-paper");
462
- const embedded = window.parent !== window;
463
-
464
- function setPaper(on) {
465
- document.body.classList.toggle("paper", on);
466
- if (on) {
467
- hostTheme = root.getAttribute("data-theme");
468
- root.setAttribute("data-theme", "light");
469
- } else if (hostTheme) {
470
- root.setAttribute("data-theme", hostTheme);
471
- } else {
472
- root.removeAttribute("data-theme");
473
- }
474
- btn.textContent = on ? "Edit view" : "Paper view";
475
- document.getElementById("exportbar").hidden = !on || embedded;
476
- if (afterToggle) requestAnimationFrame(afterToggle);
477
- postHeight();
478
- }
479
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
480
- // Embedded, the host page owns these controls — they sit beside the panel's
481
- // static-figure toggle and are driven from the outside. Standalone (the
482
- // shareable single file) this page needs its own.
483
- if (embedded) document.querySelector(".viewbar").hidden = true;
484
- window.addEventListener("message", ev => {
485
- const d = ev.data;
486
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
487
- });
488
- // Only standalone: embedded, the host owns the button and would not see the
489
- // key press, so its label would fall out of step with the frame.
490
- if (!embedded) {
491
- document.addEventListener("keydown", ev => {
492
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
493
- });
494
- }
495
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
496
- }
497
-
498
- // --- Figure export ---------------------------------------------------------
499
- // The chart is live SVG, so a file can be built from it directly. Three things
500
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
501
- // nothing outside this document), it has no background or font of its own, and
502
- // the legend is HTML rather than part of the SVG.
503
-
504
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
505
- // cloned and its label re-emitted at the measured position. foreignObject would
506
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
507
- // break the PNG path.
508
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
509
- // once the node leaves this document.
510
- function resolveVars(root, resolve) {
511
- for (const node of [root, ...root.querySelectorAll("*")]) {
512
- for (const attr of ["fill", "stroke"]) {
513
- const value = node.getAttribute(attr);
514
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
515
- }
516
- }
517
- }
518
-
519
- function legendToSvg(container, resolve) {
520
- const base = container.getBoundingClientRect();
521
- const group = document.createElementNS(NS, "g");
522
- let height = 0;
523
- for (const item of container.querySelectorAll(".item")) {
524
- const box = item.getBoundingClientRect();
525
- if (!box.width) continue;
526
- height = Math.max(height, box.bottom - base.top);
527
- let textLeft = box.left - base.left;
528
- const glyph = item.querySelector("svg");
529
- if (glyph) {
530
- const gbox = glyph.getBoundingClientRect();
531
- const wrap = el("g", {
532
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
533
- }, group);
534
- const glyphClone = glyph.cloneNode(true);
535
- resolveVars(glyphClone, resolve);
536
- wrap.appendChild(glyphClone);
537
- textLeft = gbox.right - base.left + 5;
538
- }
539
- const label = item.textContent.trim();
540
- if (!label) continue;
541
- const colored = item.querySelector("[style*='color']");
542
- const text = el("text", {
543
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
544
- fill: resolve(getComputedStyle(colored || item).color),
545
- }, group);
546
- text.textContent = label;
547
- }
548
- return { group, height: Math.ceil(height) };
549
- }
550
-
551
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
552
- // y-axis beside the scrolling plot) still exports as one figure.
553
- function buildExportSvg(parts, legendEl, pad = 10) {
554
- const rootStyle = getComputedStyle(document.documentElement);
555
- const resolve = value => String(value).replace(
556
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
557
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
558
-
559
- let chartW = 0, chartH = 0;
560
- for (const part of parts) {
561
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
562
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
563
- }
564
-
565
- const out = document.createElementNS(NS, "svg");
566
- out.setAttribute("xmlns", NS);
567
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
568
- let top = pad;
569
- const later = []; // built after the width is known
570
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
571
- if (legend && legend.height) {
572
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
573
- later.push(() => out.appendChild(legend.group));
574
- top += legend.height + 8;
575
- }
576
-
577
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
578
- const height = top + chartH + pad;
579
- out.setAttribute("width", Math.ceil(width));
580
- out.setAttribute("height", Math.ceil(height));
581
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
582
- for (const build of later) build();
583
-
584
- for (const part of parts) {
585
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
586
- const clone = part.svg.cloneNode(true);
587
- resolveVars(clone, resolve);
588
- while (clone.firstChild) group.appendChild(clone.firstChild);
589
- }
590
- return out;
591
- }
592
-
593
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
594
- function slugify(text) {
595
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
596
- }
597
-
598
- function downloadUrl(url, filename) {
599
- const a = document.createElement("a");
600
- a.href = url;
601
- a.download = filename;
602
- document.body.appendChild(a);
603
- a.click();
604
- a.remove();
605
- }
606
-
607
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
608
- function rasterize(svg, scale, done, fail) {
609
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
610
- const img = new Image();
611
- img.onload = () => {
612
- const canvas = document.createElement("canvas");
613
- canvas.width = Math.round(width * scale);
614
- canvas.height = Math.round(height * scale);
615
- const ctx = canvas.getContext("2d");
616
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
617
- ctx.drawImage(img, 0, 0);
618
- done(canvas, width, height);
619
- };
620
- img.onerror = fail;
621
- img.src = "data:image/svg+xml;charset=utf-8,"
622
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
623
- }
624
-
625
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
626
- // cost this page its zero-dependency, single-file property. The image is stored
627
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
628
- // in points to the figure's CSS size, so it prints at the size it appears here
629
- // and the pixels land at 96*scale dpi.
630
- async function buildPdf(canvas, cssWidth, cssHeight) {
631
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
632
- const rgb = new Uint8Array((pixels.length / 4) * 3);
633
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
634
- rgb[j] = pixels[i];
635
- rgb[j + 1] = pixels[i + 1];
636
- rgb[j + 2] = pixels[i + 2];
637
- }
638
- const deflated = new Uint8Array(await new Response(
639
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
640
-
641
- const encoder = new TextEncoder();
642
- const chunks = [];
643
- const offsets = [];
644
- let cursor = 0;
645
- const put = data => {
646
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
647
- chunks.push(bytes);
648
- cursor += bytes.length;
649
- };
650
- const object = (id, body, stream) => {
651
- offsets[id] = cursor;
652
- put(`${id} 0 obj\n${body}\n`);
653
- if (stream) {
654
- put("stream\n");
655
- put(stream);
656
- put("\nendstream\n");
657
- }
658
- put("endobj\n");
659
- };
660
-
661
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
662
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
663
- put("%PDF-1.4\n");
664
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
665
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
666
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
667
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
668
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
669
- object(4, "<< /Type /XObject /Subtype /Image "
670
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
671
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
672
- object(5, `<< /Length ${content.length} >>`, content);
673
-
674
- const xref = cursor;
675
- let table = "xref\n0 6\n0000000000 65535 f \n";
676
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
677
- put(table);
678
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
679
- return new Blob(chunks, { type: "application/pdf" });
680
- }
681
-
682
- // Wire up the export buttons; `getParts` is called per click so the file always
683
- // matches what is on screen. Returns a `run(format)` the host page can drive.
684
- function setUpExport(getParts, basename) {
685
- const buttons = {
686
- svg: document.getElementById("btn-svg"),
687
- png: document.getElementById("btn-png"),
688
- pdf: document.getElementById("btn-pdf"),
689
- };
690
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
691
-
692
- // A sandboxed frame has no modals, so a failure is reported on the button.
693
- function complain(format) {
694
- const button = buttons[format];
695
- if (!button) return;
696
- const label = button.textContent;
697
- button.textContent = "failed";
698
- setTimeout(() => { button.textContent = label; }, 2500);
699
- }
700
-
701
- function run(format) {
702
- const svg = figure();
703
- const name = basename();
704
- if (format === "svg") {
705
- downloadUrl("data:image/svg+xml;charset=utf-8,"
706
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
707
- return;
708
- }
709
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
710
- // points so the pixels already land near 200 dpi at print size.
711
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
712
- if (format === "png") {
713
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
714
- } else {
715
- buildPdf(canvas, cssWidth, cssHeight)
716
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
717
- .catch(() => complain("pdf"));
718
- }
719
- }, () => complain(format));
720
- }
721
-
722
- for (const format of Object.keys(buttons)) {
723
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
724
- }
725
- // Embedded, the buttons live in the host's panel header (see main.taExport).
726
- window.addEventListener("message", ev => {
727
- const d = ev.data;
728
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
729
- });
730
- }
731
-
732
- // When embedded, report the content height so the host page can size the
733
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
734
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
735
- // clamped to at least the iframe's current viewport, which turns the
736
- // resize round-trip into a grow-forever feedback loop. The change guard
737
- // stops re-posting once the height settles.
738
- let lastPostedHeight = 0;
739
- function postHeight() {
740
- if (window.parent === window) return;
741
- const height = Math.ceil(document.body.offsetHeight);
742
- if (Math.abs(height - lastPostedHeight) < 3) return;
743
- lastPostedHeight = height;
744
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
745
- }
746
-
747
-
748
- // POINTS is one record per matrix label (its model, variant, family and mean
749
- // win rate); the matrix itself is CONFIG.matrix, row-major over CONFIG.methods.
750
- const METHODS = CONFIG.methods;
751
- const MATRIX = CONFIG.matrix;
752
- const rowOf = new Map(METHODS.map((m, i) => [m, i]));
753
- const info = new Map(POINTS.map(p => [p.method, p]));
754
- const MODELS = [...new Set(POINTS.map(p => p.model))];
755
- const VARIANTS = CONFIG.variants.filter(v => POINTS.some(p => p.variant === v));
756
- const VARIANT_BTN = { "Tuned + Ens.": "Tuned + Ensembled" };
757
-
758
- // Reference pipelines are AutoML systems rather than models, so a first read of
759
- // the matrix should be model against model; they are one chip click away. Falls
760
- // back to everything if that would leave nothing to compare.
761
- const OFF_BY_DEFAULT = new Set(["Reference Pipeline"]);
762
- const familyOf = new Map(POINTS.map(p => [p.model, p.family]));
763
- const DEFAULT_MODELS = MODELS.filter(m => !OFF_BY_DEFAULT.has(familyOf.get(m)));
764
-
765
- const state = {
766
- models: new Set(DEFAULT_MODELS.length > 1 ? DEFAULT_MODELS : MODELS),
767
- variants: new Set(VARIANTS),
768
- // One entry per model by default, as the static figure shows it: 80 rows of
769
- // model-variant pairs is a wall, and the comparison people want first is
770
- // between models.
771
- best: true,
772
- order: "published",
773
- // Default to fitting the panel: at reading size the matrix is taller than a
774
- // screen, and the shape of the heatmap is what most readers want first.
775
- zoom: "fit",
776
- };
777
- // Ceiling on the fitted height. Fitting is normally width-driven; this only bites
778
- // on very tall selections, and it is set where the labels are still readable
779
- // rather than at half a screen, which would shrink 30 rows to 8px type.
780
- const FIT_H = 1040;
781
- // Nominal (drawn) size of the last render; the display size is a CSS scale of it.
782
- let nomW = 0, nomH = 0;
783
- const svg = document.getElementById("chart");
784
- const wrap = document.getElementById("wrap");
785
- const tooltip = document.querySelector(".tooltip");
786
- const chipsBox = document.getElementById("chips");
787
- const famChips = new Map();
788
- const chipByModel = new Map();
789
-
790
- // Sized against the static figure rather than the other explorers: that one uses
791
- // 16pt tick labels and 18pt axis labels on generously sized cells, and it stayed
792
- // the easier of the two to read. Weight still matches the explorers' 650, so a
793
- // model name looks like the same name everywhere.
794
- const LABEL_SIZE = 16, LABEL_WEIGHT = 650, CAPTION_SIZE = 18, CELL = 42, VALUE_SIZE = 15;
795
- // The colour key is a vertical bar to the right of the matrix, as on the static
796
- // figure. It sits beside the rows rather than above them, so it is in view
797
- // wherever the reader is in a tall matrix.
798
- const BAR_W = 30, BAR_GAP = 32, TICK_SIZE = 15;
799
- const SCALE_CAPTION = "win rate of the row over the column";
800
- const SCALE_CAPTION_SHORT = "win rate";
801
-
802
- // Purple (the column wins) through white to green (the row wins), the same
803
- // diverging reading as the static figure's PRGn colormap.
804
- const LOSE = [118, 42, 131], MID = [247, 247, 247], WIN = [27, 120, 55];
805
- function cellColor(rate) {
806
- if (rate == null || !isFinite(rate)) return "#8884";
807
- const t = Math.max(0, Math.min(1, rate));
808
- const [a, b, u] = t < 0.5 ? [LOSE, MID, t / 0.5] : [MID, WIN, (t - 0.5) / 0.5];
809
- return "rgb(" + a.map((v, i) => Math.round(v + u * (b[i] - v))).join(",") + ")";
810
- }
811
-
812
- // Measured where possible: the labels are model names of wildly different
813
- // lengths, and the margins have to clear the longest one exactly or a rotated
814
- // column label runs into the caption above it. `getComputedTextLength` returns 0
815
- // when the frame has not been laid out yet (it is lazily loaded, and may still be
816
- // off-screen on the first paint), which would collapse both margins and push
817
- // every label outside the viewBox — invisible until something forced a redraw.
818
- // So fall back to an estimate, and redraw once the fonts have settled.
819
- function textWidth(text, size, weight) {
820
- const probe = el("text", {
821
- "font-size": size || LABEL_SIZE, "font-weight": weight || LABEL_WEIGHT,
822
- }, svg);
823
- probe.textContent = text;
824
- let width = 0;
825
- try { width = probe.getComputedTextLength(); } catch (e) { width = 0; }
826
- probe.remove();
827
- return width > 0 ? width : text.length * (size || LABEL_SIZE) * 0.58;
828
- }
829
-
830
- // Labels always carry their variant tag — "(default)" / "(tuned + ensembled)" —
831
- // including in one-per-model mode, where the tag is precisely what says which
832
- // variant survived the filter. The static figure labels them the same way.
833
- function labelText(label) {
834
- return label;
835
- }
836
-
837
- function shown() {
838
- let list = METHODS.filter(label => {
839
- const p = info.get(label);
840
- if (!p || !state.models.has(p.model)) return false;
841
- return !p.variant || state.variants.has(p.variant);
842
- });
843
- if (state.best) {
844
- // Best *among the variants still selected*, so the variant toggles keep
845
- // meaning something in this mode.
846
- const pick = new Map();
847
- for (const label of list) {
848
- const p = info.get(label);
849
- const held = pick.get(p.model);
850
- if (!held || (info.get(held).mean || 0) < (p.mean || 0)) pick.set(p.model, label);
851
- }
852
- list = list.filter(label => pick.get(info.get(label).model) === label);
853
- }
854
- if (state.order === "winrate") {
855
- list.sort((a, b) => (info.get(b).mean || 0) - (info.get(a).mean || 0));
856
- } else if (state.order === "name") {
857
- list.sort((a, b) => labelText(a).localeCompare(labelText(b)));
858
- }
859
- return list;
860
- }
861
-
862
- function render() {
863
- const list = shown();
864
- svg.innerHTML = "";
865
- if (list.length < 2) {
866
- svg.setAttribute("width", 0);
867
- svg.setAttribute("height", 0);
868
- // Also drop the display scale, or the empty chart keeps the last figure's box.
869
- svg.style.width = svg.style.height = "";
870
- nomW = nomH = 0;
871
- wrap.querySelector(".wr-empty") ||
872
- wrap.insertAdjacentHTML("beforeend",
873
- '<p class="wr-empty">Select at least two models to compare.</p>');
874
- postHeight();
875
- return;
876
- }
877
- const empty = wrap.querySelector(".wr-empty");
878
- if (empty) empty.remove();
879
-
880
- const widths = list.map(label => textWidth(labelText(label)));
881
- const longest = Math.max(...widths);
882
- // A label rotated -60° rises 0.866 of its length; the captions sit in the
883
- // corner box rather than above the columns, where long names would cross them.
884
- // Floor the header so the two corner captions always have room, even when
885
- // every selected name is short.
886
- const headerH = Math.max(Math.ceil(longest * 0.866) + 14, 2 * CAPTION_SIZE + 26);
887
- const labelW = Math.ceil(longest) + 16;
888
- const n = list.length;
889
- // A -60° label anchored at (x, y) reaches (x + 0.5L, y - 0.866L): it grows up
890
- // *and right*. headerH covers the rise; this covers the run, or the rightmost
891
- // column's name is clipped by half its length.
892
- const overhang = Math.ceil(longest * 0.5);
893
- const matrixRight = labelW + n * CELL;
894
- // The key occupies the band to the right of the matrix; the label overhang is
895
- // above the matrix, so the two never collide and the wider of the two wins.
896
- const tickW = Math.ceil(textWidth("100%", TICK_SIZE, 700));
897
- const barX = matrixRight + BAR_GAP;
898
- const keyRight = barX + BAR_W + 12 + tickW + 10 + CAPTION_SIZE;
899
- const width = Math.max(matrixRight + overhang + 12, keyRight + 10);
900
- const height = headerH + n * CELL + 12;
901
- svg.setAttribute("width", width);
902
- svg.setAttribute("height", height);
903
- svg.setAttribute("viewBox", "0 0 " + width + " " + height);
904
-
905
- // Captions, stacked in the corner box and bold, as on the static figure.
906
- const capB = el("text", {
907
- x: 4, y: headerH - 34, "font-size": CAPTION_SIZE, "font-weight": 700, fill: "var(--ink)",
908
- }, svg);
909
- capB.textContent = "Model B: loser →";
910
- const capA = el("text", {
911
- x: 4, y: headerH - 14, "font-size": CAPTION_SIZE, "font-weight": 700, fill: "var(--ink)",
912
- }, svg);
913
- capA.textContent = "Model A: winner ↓";
914
-
915
- list.forEach((label, j) => {
916
- const x = labelW + j * CELL + CELL / 2 + 4;
917
- const text = el("text", {
918
- x: x, y: headerH - 8, "font-size": LABEL_SIZE, "font-weight": LABEL_WEIGHT,
919
- "text-anchor": "start", fill: FAM_INK[info.get(label).family] || "var(--ink)",
920
- transform: "rotate(-60 " + x + " " + (headerH - 8) + ")", cursor: "pointer",
921
- }, svg);
922
- text.textContent = labelText(label);
923
- text.addEventListener("click", () => toggleModel(info.get(label).model));
924
- });
925
-
926
- list.forEach((rowLabel, i) => {
927
- const y = headerH + i * CELL;
928
- const text = el("text", {
929
- x: labelW - 12, y: y + CELL / 2 + 6, "font-size": LABEL_SIZE, "font-weight": LABEL_WEIGHT,
930
- "text-anchor": "end", fill: FAM_INK[info.get(rowLabel).family] || "var(--ink)",
931
- cursor: "pointer",
932
- }, svg);
933
- text.textContent = labelText(rowLabel);
934
- text.addEventListener("click", () => toggleModel(info.get(rowLabel).model));
935
-
936
- list.forEach((colLabel, j) => {
937
- const rate = valueAt(rowLabel, colLabel);
938
- const x = labelW + j * CELL;
939
- const same = rowLabel === colLabel;
940
- const rect = el("rect", {
941
- x: x, y: y, width: CELL - 1, height: CELL - 1, rx: 2,
942
- fill: same ? "#8883" : cellColor(rate),
943
- }, svg);
944
- if (same) return;
945
- const value = el("text", {
946
- x: x + (CELL - 1) / 2, y: y + CELL / 2 + 5.5, "font-size": VALUE_SIZE,
947
- "font-weight": 600, "text-anchor": "middle",
948
- fill: Math.abs(rate - 0.5) > 0.3 ? "#ffffff" : "#14161a", "pointer-events": "none",
949
- }, svg);
950
- value.textContent = rate == null || !isFinite(rate) ? "" : Math.round(rate * 100);
951
- rect.addEventListener("mousemove", ev => showTip(ev, rowLabel, colLabel, rate));
952
- rect.addEventListener("mouseleave", () => { tooltip.style.display = "none"; });
953
- });
954
- });
955
-
956
- drawScale(barX, headerH, n * CELL - 2, tickW);
957
- nomW = width;
958
- nomH = height;
959
- applyZoom();
960
- }
961
-
962
- // Scale the figure for display only: the width/height *attributes* stay nominal,
963
- // so the export helpers keep building full-size figures, and the CSS box drives
964
- // what the page shows (the viewBox does the scaling, so it stays vector-sharp).
965
- function applyZoom() {
966
- if (!nomW || !nomH) return;
967
- let scale = Number(state.zoom);
968
- if (!(scale > 0)) {
969
- const availW = Math.max(240, (wrap.clientWidth || nomW) - 4);
970
- scale = Math.min(1, availW / nomW, FIT_H / nomH);
971
- }
972
- svg.style.width = Math.round(nomW * scale) + "px";
973
- svg.style.height = Math.round(nomH * scale) + "px";
974
- postHeight();
975
- }
976
-
977
- // The colour key, drawn into the figure so it is there in paper view and in an
978
- // exported SVG/PNG/PDF, not only beside the live chart.
979
- function drawScale(x, y, barH, tickW) {
980
- const id = "wr-ramp";
981
- const grad = el("linearGradient", { id: id, x1: "0", x2: "0", y1: "0", y2: "1" },
982
- el("defs", {}, svg));
983
- // Top of the bar is the row winning outright, the bottom is the column winning.
984
- for (const [offset, rate] of [[0, 1], [0.5, 0.5], [1, 0]]) {
985
- el("stop", { offset: offset, "stop-color": cellColor(rate) }, grad);
986
- }
987
- el("rect", {
988
- x: x, y: y, width: BAR_W, height: barH, rx: 7,
989
- fill: "url(#" + id + ")", stroke: "var(--muted)", "stroke-width": 1,
990
- }, svg);
991
- for (const [frac, label] of [[0, "100%"], [0.5, "50%"], [1, "0%"]]) {
992
- const ty = y + frac * barH;
993
- el("line", {
994
- x1: x + BAR_W, y1: ty, x2: x + BAR_W + 6, y2: ty,
995
- stroke: "var(--muted)", "stroke-width": 1.5,
996
- }, svg);
997
- const tick = el("text", {
998
- x: x + BAR_W + 11, y: ty + TICK_SIZE * 0.36, "font-size": TICK_SIZE,
999
- "font-weight": 700, "text-anchor": "start", fill: "var(--ink)",
1000
- }, svg);
1001
- tick.textContent = label;
1002
- }
1003
- // The label reads down the bar. It shortens rather than growing past the bar
1004
- // when only a couple of models are selected: anything above the bar would run
1005
- // into the rightmost column's rotated name.
1006
- const size = CAPTION_SIZE - 2;
1007
- const cx = x + BAR_W + 11 + tickW + 10 + size * 0.5;
1008
- const cy = y + barH / 2;
1009
- const caption = el("text", {
1010
- x: cx, y: cy, "font-size": size, "font-weight": 700,
1011
- "text-anchor": "middle", fill: "var(--ink)",
1012
- transform: "rotate(90 " + cx + " " + cy + ")",
1013
- }, svg);
1014
- caption.textContent =
1015
- textWidth(SCALE_CAPTION, size, 700) <= barH ? SCALE_CAPTION : SCALE_CAPTION_SHORT;
1016
- }
1017
-
1018
- function valueAt(rowLabel, colLabel) {
1019
- const i = rowOf.get(rowLabel), j = rowOf.get(colLabel);
1020
- if (i == null || j == null) return null;
1021
- const row = MATRIX[i];
1022
- return row ? row[j] : null;
1023
- }
1024
-
1025
- function showTip(ev, rowLabel, colLabel, rate) {
1026
- tooltip.style.display = "block";
1027
- tooltip.innerHTML =
1028
- '<div class="t-name">' + rowLabel + " vs " + colLabel + "</div>" +
1029
- "<div>" + rowLabel + " wins " + fmtNum(rate * 100, 1) + "% of tasks</div>" +
1030
- '<div class="t-var">' + colLabel + " wins " + fmtNum((1 - rate) * 100, 1) + "%</div>";
1031
- const box = document.body.getBoundingClientRect();
1032
- tooltip.style.left = (ev.clientX - box.left + 14) + "px";
1033
- tooltip.style.top = (ev.clientY - box.top + 12) + "px";
1034
- }
1035
-
1036
- // ---------- model chips, by family ----------
1037
- function familyModels(fam) {
1038
- return [...new Set(POINTS.filter(p => p.family === fam).map(p => p.model))];
1039
- }
1040
- function meanOfModel(model) {
1041
- const values = POINTS.filter(p => p.model === model && isFinite(p.mean)).map(p => p.mean);
1042
- return values.length ? Math.max(...values) : 0;
1043
- }
1044
- function buildChips() {
1045
- const head = document.createElement("div");
1046
- head.className = "chips-head";
1047
- head.textContent = "Models compared — click to remove, click a family to toggle the whole group";
1048
- chipsBox.appendChild(head);
1049
- for (const fam of FAM_ORDER) {
1050
- const members = familyModels(fam);
1051
- if (!members.length) continue;
1052
- members.sort((a, b) => meanOfModel(b) - meanOfModel(a));
1053
- const row = document.createElement("div");
1054
- row.className = "chiprow";
1055
- const famBtn = document.createElement("button");
1056
- famBtn.className = "famchip";
1057
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1058
- famBtn.innerHTML = famChipLabel(fam, members.length);
1059
- famBtn.title = "Toggle all " + members.length + " " + fam + " models";
1060
- famBtn.addEventListener("click", () => toggleFamily(fam));
1061
- row.appendChild(famBtn);
1062
- famChips.set(fam, famBtn);
1063
- const set = document.createElement("div");
1064
- set.className = "chipset";
1065
- for (const model of members) {
1066
- const b = document.createElement("button");
1067
- b.className = "chip";
1068
- b.style.setProperty("--fam", FAM_VAR[fam]);
1069
- b.innerHTML = '<span class="dot"></span><span></span>';
1070
- b.lastChild.textContent = model;
1071
- b.title = model + " — best mean win rate " + fmtNum(meanOfModel(model) * 100, 1) + "%";
1072
- b.addEventListener("click", () => toggleModel(model));
1073
- set.appendChild(b);
1074
- chipByModel.set(model, b);
1075
- }
1076
- row.appendChild(set);
1077
- chipsBox.appendChild(row);
1078
- }
1079
- }
1080
- function syncChips() {
1081
- for (const [model, b] of chipByModel) b.setAttribute("aria-pressed", String(state.models.has(model)));
1082
- for (const [fam, b] of famChips) {
1083
- b.setAttribute("aria-pressed", String(familyModels(fam).every(m => state.models.has(m))));
1084
- }
1085
- }
1086
- function toggleModel(model) {
1087
- if (state.models.has(model)) state.models.delete(model); else state.models.add(model);
1088
- syncChips();
1089
- render();
1090
- }
1091
- function toggleFamily(fam) {
1092
- const members = familyModels(fam);
1093
- const allOn = members.every(m => state.models.has(m));
1094
- for (const m of members) { if (allOn) state.models.delete(m); else state.models.add(m); }
1095
- syncChips();
1096
- render();
1097
- }
1098
-
1099
- // ---------- variant toggles ----------
1100
- const variantBtns = new Map();
1101
- function buildVariantBtns() {
1102
- const box = document.getElementById("variant-btns");
1103
- for (const v of VARIANTS) {
1104
- const b = document.createElement("button");
1105
- b.className = "btn toggle";
1106
- b.innerHTML = '<span class="swatch"></span>';
1107
- // Spelled out: the matrix labels carry the long form the data uses, so the
1108
- // toggle should not be the only place showing the abbreviated key.
1109
- b.appendChild(document.createTextNode(VARIANT_BTN[v] || v));
1110
- b.style.setProperty("--fam", VARIANT_VAR[v] || "var(--accent)");
1111
- b.title = "Show or hide the " + (VARIANT_BTN[v] || v).toLowerCase() + " results";
1112
- b.addEventListener("click", () => {
1113
- if (state.variants.has(v)) state.variants.delete(v); else state.variants.add(v);
1114
- syncVariantBtns();
1115
- render();
1116
- });
1117
- box.appendChild(b);
1118
- variantBtns.set(v, b);
1119
- }
1120
- }
1121
- function syncVariantBtns() {
1122
- for (const [v, b] of variantBtns) b.setAttribute("aria-pressed", String(state.variants.has(v)));
1123
- }
1124
-
1125
- // ---------- boot ----------
1126
- if (CONFIG.title) document.getElementById("title").textContent = CONFIG.title;
1127
- document.getElementById("zoom-select").addEventListener("change", ev => {
1128
- state.zoom = ev.target.value;
1129
- applyZoom();
1130
- });
1131
- window.addEventListener("resize", applyZoom);
1132
- // Embedded in a lazily shown panel, the first render can measure a zero-width
1133
- // panel; re-fit once it has a width. Width changes only, so the height applyZoom
1134
- // itself produces cannot feed back into another fit.
1135
- if (window.ResizeObserver) {
1136
- let lastWidth = 0;
1137
- new ResizeObserver(() => {
1138
- const w = wrap.clientWidth;
1139
- if (Math.abs(w - lastWidth) < 2) return;
1140
- lastWidth = w;
1141
- applyZoom();
1142
- }).observe(wrap);
1143
- }
1144
- document.getElementById("order-select").addEventListener("change", ev => {
1145
- state.order = ev.target.value;
1146
- render();
1147
- });
1148
- const bestBtn = document.getElementById("btn-best");
1149
- bestBtn.style.setProperty("--fam", "var(--accent)");
1150
- bestBtn.addEventListener("click", () => {
1151
- state.best = !state.best;
1152
- bestBtn.setAttribute("aria-pressed", String(state.best));
1153
- render();
1154
- });
1155
- bestBtn.setAttribute("aria-pressed", String(state.best));
1156
- setUpPaperView(render);
1157
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(CONFIG.title || document.title));
1158
- buildVariantBtns();
1159
- buildChips();
1160
- syncChips();
1161
- syncVariantBtns();
1162
- render();
1163
- // The first render may have had to estimate label widths (see textWidth); redraw
1164
- // with real measurements as soon as the fonts are in.
1165
- if (document.fonts && document.fonts.ready) document.fonts.ready.then(render);
1166
- })();
1167
- </script>
1168
- </body>
1169
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/winrate_matrix.csv DELETED
@@ -1,29 +0,0 @@
1
- ,TabFM (default),TabPFN-3 (default),TabPFN-2.6 (default),TabICLv2 (default),RealTabPFN-2.5 (T+E),RealMLP (T+E),TabDPT-Turbo (default),TabDPT (T+E),TabM (T+E),LightGBM (T+E),CatBoost (T+E),iLTM (T+E),ModernNCA (T+E),LimiX (default),XGBoost (T+E),TabSwift (default),xRFM (T+E),ChimeraBoost (T+E),SAP-RPT-OSS (default),TorchMLP (T+E),EBM (T+E),ExtraTrees (T+E),FastaiMLP (T+E),RandomForest (T+E),TabSTAR (T+E),PerpetualBooster (T+E),KNN (T+E),Linear (T+E)
2
- TabFM (default),,0.78779954,0.83246195,0.84466255,0.8332244,0.86710244,0.90392154,0.87777776,0.9117648,0.9076254,0.9030501,0.9015251,0.9113289,0.9027234,0.9257082,0.89956427,0.91938996,0.921133,0.9093683,0.9464053,0.93333346,0.9507625,0.92265797,0.9607843,0.9008715,0.9897604,0.9607843,0.9747277
3
- TabPFN-3 (default),0.21220043,,0.6313726,0.5980392,0.6503269,0.7633987,0.80958617,0.7507626,0.80980396,0.8141612,0.8172114,0.8405228,0.85446614,0.7888888,0.8476035,0.9067539,0.85599136,0.86601305,0.8488018,0.90958613,0.87298477,0.9424837,0.904684,0.93681926,0.90283227,0.9474945,0.94967324,0.95337695
4
- TabPFN-2.6 (default),0.16753814,0.36862746,,0.46732023,0.49847487,0.7224402,0.6877995,0.68823534,0.76753825,0.7921569,0.80305016,0.8135076,0.82113296,0.77429193,0.83267975,0.8226579,0.83572984,0.8614379,0.8477124,0.8941176,0.88801754,0.9265796,0.8862745,0.9328976,0.89215684,0.96274513,0.95424837,0.95250547
5
- TabICLv2 (default),0.15533769,0.40196082,0.53267974,,0.54967326,0.6989107,0.7287582,0.73671025,0.75664496,0.7668846,0.7697169,0.78213507,0.7897604,0.7700435,0.78605676,0.8444445,0.79368204,0.79389983,0.81590414,0.8474946,0.8276689,0.89204806,0.8697169,0.89912856,0.8808279,0.9300654,0.9529411,0.956427
6
- RealTabPFN-2.5 (T+E),0.16677561,0.34967318,0.50152504,0.45032677,,0.6901961,0.6529412,0.6607843,0.73681915,0.76906323,0.7671025,0.7611111,0.74553376,0.7605664,0.8139434,0.80457515,0.81111115,0.806427,0.8238563,0.87734205,0.8640524,0.8827887,0.8763618,0.9089324,0.8862745,0.9679738,0.93224394,0.9346405
7
- RealMLP (T+E),0.1328976,0.23660132,0.2775599,0.30108938,0.30980387,,0.5185185,0.5011983,0.5873638,0.6832244,0.6779956,0.7211328,0.64204794,0.62461877,0.7673203,0.63485837,0.7873638,0.7009803,0.74400884,0.8043574,0.8021787,0.8533769,0.89727676,0.8949891,0.8551198,0.94858384,0.9296296,0.943573
8
- TabDPT-Turbo (default),0.09607843,0.19041394,0.31220043,0.2712418,0.34705883,0.4814814,,0.49716777,0.527342,0.5732026,0.6126362,0.63224405,0.5834422,0.6368191,0.6366013,0.7221133,0.66427016,0.6688453,0.6799565,0.7461873,0.68169934,0.8277778,0.8,0.84596956,0.817865,0.88976043,0.9220044,0.87102413
9
- TabDPT (T+E),0.12222224,0.2492375,0.31176475,0.26328975,0.3392157,0.49880174,0.5028323,,0.54651415,0.58736384,0.5869281,0.6372549,0.5880174,0.59226584,0.61383444,0.6667755,0.69008714,0.65337694,0.69847494,0.73061013,0.7038127,0.81111115,0.7825708,0.845316,0.80087155,0.9117648,0.9208061,0.8586056
10
- TabM (T+E),0.0882353,0.19019604,0.23246187,0.24335514,0.2631808,0.41263613,0.472658,0.45348582,,0.5718955,0.58779955,0.58442265,0.56862754,0.52657944,0.6440087,0.60370374,0.6080611,0.6732026,0.6956428,0.7422659,0.7629631,0.775817,0.81633985,0.787146,0.85294116,0.89193904,0.9067539,0.93769056
11
- LightGBM (T+E),0.092374735,0.18583877,0.20784314,0.23311546,0.23093684,0.3167756,0.42679742,0.41263622,0.42810455,,0.51437914,0.5253813,0.5034858,0.47668847,0.7058825,0.58387804,0.59128547,0.6857298,0.6505447,0.68583876,0.75370383,0.7979304,0.76688445,0.8446623,0.83986926,0.93137264,0.9183006,0.93050116
12
- CatBoost (T+E),0.0969499,0.18278866,0.19694988,0.2302832,0.23289761,0.32200435,0.38736382,0.4130719,0.41220048,0.4856209,,0.532244,0.533769,0.48104575,0.6332244,0.57930285,0.59629625,0.66862744,0.675817,0.6973856,0.7294118,0.796732,0.76274496,0.84923756,0.8387799,0.90740734,0.91437906,0.9172113
13
- iLTM (T+E),0.09847494,0.15947711,0.1864924,0.2178649,0.23888883,0.27886707,0.367756,0.36274514,0.41557735,0.47461873,0.46775594,,0.48779953,0.4576253,0.5728758,0.54596955,0.5859478,0.59999996,0.6542484,0.7239652,0.6869281,0.78311557,0.8023966,0.8392158,0.8092593,0.8764707,0.9041394,0.8884533
14
- ModernNCA (T+E),0.08867102,0.14553379,0.17886713,0.21023968,0.25446624,0.35795215,0.41655773,0.4119826,0.43137252,0.49651423,0.466231,0.5122005,,0.5206972,0.574292,0.5703704,0.5699346,0.5884531,0.6220044,0.65925926,0.672767,0.7470587,0.7244008,0.7753813,0.76949894,0.8492375,0.8700436,0.83769053
15
- LimiX (default),0.097276695,0.21111111,0.22570808,0.22995642,0.23943357,0.3753812,0.3631808,0.40773416,0.4734205,0.52331156,0.5189543,0.5423747,0.47930282,,0.55468404,0.50675386,0.5180828,0.62178653,0.6102397,0.6187363,0.67472774,0.6623093,0.69117653,0.69564265,0.7529412,0.74422663,0.8154685,0.86710244
16
- XGBoost (T+E),0.074291945,0.15239652,0.16732025,0.21394335,0.18605666,0.23267974,0.36339873,0.3861656,0.35599133,0.29411763,0.3667756,0.4271242,0.42570803,0.44531593,,0.5084967,0.54912853,0.5625272,0.6069717,0.6589324,0.68344223,0.75533766,0.7252723,0.8282136,0.81851864,0.8570806,0.89825696,0.9141612
17
- TabSwift (default),0.100435734,0.0932462,0.17734204,0.15555556,0.19542484,0.36514163,0.27788672,0.33322442,0.3962963,0.41612196,0.42069712,0.45403045,0.4296296,0.4932462,0.49150324,,0.51677555,0.514597,0.61089325,0.6148148,0.60348576,0.6795208,0.7614379,0.69564265,0.812854,0.83834434,0.8732026,0.85076255
18
- xRFM (T+E),0.08061002,0.14400873,0.16427013,0.2063181,0.18888889,0.21263619,0.33572984,0.30991286,0.39193892,0.4087146,0.4037037,0.41405225,0.43006533,0.48191726,0.4508715,0.48322442,,0.5030501,0.55795205,0.587146,0.61633986,0.7108933,0.69248366,0.7629629,0.75925916,0.8089325,0.922549,0.909586
19
- ChimeraBoost (T+E),0.07886711,0.13398692,0.13856208,0.20610018,0.193573,0.29901958,0.33115464,0.3466231,0.32679737,0.31427017,0.3313726,0.4,0.41154686,0.3782135,0.43747276,0.48540303,0.49694988,,0.5867102,0.58976036,0.64313734,0.66339874,0.6562092,0.72745097,0.8013072,0.814597,0.83289754,0.8795207
20
- SAP-RPT-OSS (default),0.09063181,0.15119825,0.15228757,0.18409587,0.1761438,0.2559913,0.32004356,0.3015251,0.30435732,0.34945533,0.32418305,0.34575164,0.3779956,0.3897603,0.3930283,0.38910675,0.44204798,0.41328976,,0.5030501,0.5071896,0.5803922,0.6381264,0.59901965,0.6921569,0.7361656,0.8058824,0.87450975
21
- TorchMLP (T+E),0.053594775,0.09041394,0.10588236,0.15250546,0.122657955,0.1956427,0.25381267,0.26938996,0.2577342,0.31416118,0.3026144,0.27603483,0.34074074,0.38126364,0.34106752,0.38518515,0.412854,0.41023964,0.4969499,,0.5,0.66056645,0.68213516,0.70065355,0.7874729,0.7250545,0.8413943,0.8798476
22
- EBM (T+E),0.06666666,0.12701525,0.11198258,0.17233114,0.13594772,0.19782133,0.31830066,0.2961873,0.23703705,0.2462963,0.27058825,0.31307185,0.3272331,0.32527232,0.31655777,0.3965142,0.38366014,0.35686275,0.49281046,0.5,,0.6171024,0.57592595,0.63159037,0.7851852,0.7061002,0.8313727,0.904793
23
- ExtraTrees (T+E),0.049237475,0.057516348,0.07342048,0.10795209,0.11721134,0.14662309,0.17222221,0.1888889,0.22418296,0.20206973,0.20326798,0.21688454,0.2529412,0.33769068,0.24466233,0.3204793,0.2891068,0.3366013,0.41960785,0.33943355,0.3828976,,0.49389976,0.6332244,0.6583878,0.64444447,0.88583875,0.7785402
24
- FastaiMLP (T+E),0.077342056,0.0953159,0.113725506,0.13028322,0.12363834,0.102723315,0.20000002,0.2174292,0.18366013,0.23311546,0.23725493,0.19760346,0.2755991,0.3088235,0.27472764,0.23856208,0.30751634,0.34379083,0.3618736,0.31786492,0.42407405,0.50610024,,0.56045747,0.6694989,0.6150327,0.7636166,0.86056644
25
- RandomForest (T+E),0.039215688,0.06318083,0.0671024,0.100871466,0.091067545,0.10501088,0.15403052,0.15468411,0.21285401,0.15533769,0.15076254,0.1607843,0.22461875,0.30435732,0.1717865,0.30435726,0.23703706,0.272549,0.40098038,0.2993464,0.36840957,0.3667756,0.4395425,,0.654902,0.6137255,0.8453159,0.76753813
26
- TabSTAR (T+E),0.099128544,0.09716776,0.10784314,0.11917212,0.11372549,0.14488018,0.18213508,0.19912857,0.14705883,0.16013072,0.16122004,0.19074073,0.23050106,0.24705885,0.18148148,0.18714596,0.24074076,0.19869278,0.30784312,0.21252722,0.2148148,0.34161216,0.33050108,0.34509805,,0.5320262,0.6283224,0.67755985
27
- PerpetualBooster (T+E),0.010239651,0.052505445,0.037254903,0.06993464,0.032026142,0.05141612,0.11023965,0.088235304,0.108061016,0.068627454,0.092592604,0.12352943,0.1507625,0.25577345,0.1429194,0.16165577,0.19106753,0.18540302,0.26383442,0.27494553,0.29389977,0.35555556,0.38496733,0.38627446,0.46797383,,0.61612195,0.68562084
28
- KNN (T+E),0.039215688,0.0503268,0.04575164,0.047058824,0.067756,0.07037037,0.07799564,0.0791939,0.09324619,0.08169934,0.085620925,0.09586056,0.12995644,0.18453158,0.10174292,0.1267974,0.07745098,0.16710238,0.19411764,0.1586057,0.16862746,0.11416123,0.23638344,0.15468408,0.37167758,0.38387802,,0.6224401
29
- Linear (T+E),0.025272332,0.0466231,0.047494557,0.04357299,0.06535949,0.056427017,0.12897603,0.14139433,0.06230937,0.06949892,0.082788676,0.111546844,0.16230936,0.13289759,0.08583879,0.14923747,0.09041394,0.120479316,0.1254902,0.12015251,0.09520697,0.22145969,0.13943355,0.23246187,0.32244012,0.31437913,0.37755993,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_all/winrate_matrix.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:ffcfbb5fff8f010ee7a55f2db2cdf1f98513fa37d75457cbeaa2755b113cc4b5
3
- size 684630
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/leaderboard_overview_explorer.html DELETED
@@ -1,1411 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard explorer — no imputation | All Tasks | Medium</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Chart: a fixed y-axis pane beside a horizontally scrolling plot pane, so
269
- the axis stays readable however far right the reader scrolls. */
270
- .lb-chartwrap { position: relative; display: flex; align-items: flex-start; }
271
- .lb-axis { flex: none; }
272
- .lb-scroll {
273
- flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lb-scroll::-webkit-scrollbar { height: 10px; }
277
- .lb-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- .rangepick input[type=range] { width: 96px; accent-color: var(--accent); cursor: pointer; }
282
- .rangeval { font-variant-numeric: tabular-nums; font-weight: 500; min-width: 3.4em; }
283
- .lb-empty { padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
284
- /* Puts the family key on its own line of the legend strip. */
285
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
286
- /* Family blocks pack side by side rather than stacking: below a full-width
287
- chart, one row per family leaves the small ones (Other, Baseline) each
288
- wasting a line. The wide families still claim a row of their own. */
289
- .chips { margin-top: 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
290
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
291
- .chiprow { flex: 0 1 auto; min-width: 0; }
292
- </style>
293
- </head>
294
- <body>
295
- <div class="viewbar">
296
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
297
- </div>
298
- <p class="explorer-title" id="title"></p>
299
- <!-- One control row above everything it scopes (axis, sorting, variants, selection). -->
300
- <div class="controls">
301
- <label class="metricpick">Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <label class="metricpick">Sort
305
- <select id="sort-select"></select>
306
- </label>
307
- <label class="metricpick rangepick" title="Raise the axis floor to spread out the top of the field">Zoom
308
- <input type="range" id="ymin-range" aria-label="Y-axis minimum">
309
- <span class="rangeval" id="ymin-val"></span>
310
- </label>
311
- <div class="btnrow" id="variant-btns"></div>
312
- <div class="btnrow">
313
- <button class="btn" id="btn-all">All methods</button>
314
- <button class="btn" id="btn-top">Top 15</button>
315
- <button class="btn" id="btn-none">Clear</button>
316
- </div>
317
- <span class="hint">Click a column or chip to remove a method &middot; hover for exact values</span>
318
- </div>
319
- <div class="exportbar" id="exportbar" hidden>
320
- <span class="hint">Export figure</span>
321
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
322
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
323
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
324
- </div>
325
- <div class="legendstrip" id="legendstrip"></div>
326
- <div class="lb-chartwrap" id="chartwrap">
327
- <svg id="axis" class="lb-axis" role="presentation"></svg>
328
- <div class="lb-scroll" id="scroller">
329
- <svg id="chart" role="img" aria-label="Leaderboard overview"></svg>
330
- </div>
331
- <div class="tooltip"></div>
332
- </div>
333
- <div class="chips" id="chips"></div>
334
- <details class="datatable">
335
- <summary>Data table</summary>
336
- <div class="tblwrap" id="tblwrap"></div>
337
- </details>
338
-
339
- <script>
340
- (function () {
341
- "use strict";
342
- const CONFIG = {"title": null, "metrics": [{"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": "", "ci": {"lo": "elo_lo", "hi": "elo_hi"}}, {"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "score", "label": "Score", "axisLabel": "Score \u2014 higher is better", "lowerBetter": false, "fromZero": true, "decimals": 3, "suffix": ""}, {"key": "rank", "label": "Average rank", "axisLabel": "Average rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}, {"key": "hrank", "label": "Harmonic rank", "axisLabel": "Harmonic rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}], "rankMetric": "elo"};
343
- const POINTS = [{"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","reference":false,"elo":1986.0,"imp":2.683,"score":0.867,"rank":4.41,"hrank":1.69,"elo_hi":2292.0,"elo_lo":1841.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1899.0,"imp":4.858,"score":0.71,"rank":6.33,"hrank":2.98,"elo_hi":1984.0,"elo_lo":1850.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","reference":false,"elo":1794.0,"imp":5.878,"score":0.659,"rank":9.55,"hrank":3.26,"elo_hi":2008.0,"elo_lo":1659.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1775.0,"imp":6.048,"score":0.579,"rank":10.23,"hrank":4.46,"elo_hi":1858.0,"elo_lo":1724.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1722.0,"imp":7.846,"score":0.504,"rank":12.39,"hrank":8.78,"elo_hi":1852.0,"elo_lo":1647.0,"imputed_pct":0.0,"imputed":false},{"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","reference":false,"elo":1697.0,"imp":6.851,"score":0.541,"rank":13.53,"hrank":4.41,"elo_hi":1883.0,"elo_lo":1587.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1646.0,"imp":8.576,"score":0.446,"rank":16.07,"hrank":8.48,"elo_hi":1788.0,"elo_lo":1542.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1625.0,"imp":8.917,"score":0.412,"rank":17.17,"hrank":11.86,"elo_hi":1717.0,"elo_lo":1533.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1615.0,"imp":9.07,"score":0.397,"rank":17.73,"hrank":12.25,"elo_hi":1719.0,"elo_lo":1510.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1611.0,"imp":8.932,"score":0.379,"rank":17.97,"hrank":9.32,"elo_hi":1696.0,"elo_lo":1548.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1600.0,"imp":9.218,"score":0.403,"rank":18.55,"hrank":10.47,"elo_hi":1726.0,"elo_lo":1501.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1596.0,"imp":9.14,"score":0.372,"rank":18.8,"hrank":9.43,"elo_hi":1694.0,"elo_lo":1530.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1589.0,"imp":10.466,"score":0.327,"rank":19.19,"hrank":13.5,"elo_hi":1657.0,"elo_lo":1538.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1576.0,"imp":9.095,"score":0.378,"rank":19.95,"hrank":9.61,"elo_hi":1679.0,"elo_lo":1475.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1558.0,"imp":10.221,"score":0.322,"rank":20.99,"hrank":15.67,"elo_hi":1628.0,"elo_lo":1506.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1548.0,"imp":10.546,"score":0.295,"rank":21.61,"hrank":14.86,"elo_hi":1629.0,"elo_lo":1493.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1544.0,"imp":10.254,"score":0.292,"rank":21.87,"hrank":13.14,"elo_hi":1690.0,"elo_lo":1457.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1533.0,"imp":10.104,"score":0.301,"rank":22.52,"hrank":14.08,"elo_hi":1638.0,"elo_lo":1444.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1530.0,"imp":10.077,"score":0.324,"rank":22.69,"hrank":14.72,"elo_hi":1666.0,"elo_lo":1451.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","reference":false,"elo":1526.0,"imp":10.809,"score":0.355,"rank":22.94,"hrank":10.98,"elo_hi":1682.0,"elo_lo":1387.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1522.0,"imp":10.287,"score":0.319,"rank":23.22,"hrank":6.34,"elo_hi":1656.0,"elo_lo":1403.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1517.0,"imp":11.684,"score":0.314,"rank":23.53,"hrank":13.31,"elo_hi":1633.0,"elo_lo":1396.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1506.0,"imp":11.603,"score":0.216,"rank":24.2,"hrank":18.44,"elo_hi":1582.0,"elo_lo":1452.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1501.0,"imp":11.102,"score":0.234,"rank":24.53,"hrank":20.07,"elo_hi":1565.0,"elo_lo":1446.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1481.0,"imp":11.089,"score":0.234,"rank":25.86,"hrank":15.63,"elo_hi":1611.0,"elo_lo":1405.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1472.0,"imp":11.215,"score":0.217,"rank":26.42,"hrank":20.56,"elo_hi":1564.0,"elo_lo":1391.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1470.0,"imp":12.146,"score":0.266,"rank":26.58,"hrank":15.47,"elo_hi":1576.0,"elo_lo":1355.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1423.0,"imp":12.482,"score":0.224,"rank":29.66,"hrank":21.18,"elo_hi":1529.0,"elo_lo":1309.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1423.0,"imp":12.112,"score":0.155,"rank":29.69,"hrank":18.8,"elo_hi":1520.0,"elo_lo":1360.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1418.0,"imp":13.176,"score":0.203,"rank":30.0,"hrank":19.33,"elo_hi":1530.0,"elo_lo":1299.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1404.0,"imp":12.662,"score":0.221,"rank":30.97,"hrank":12.53,"elo_hi":1527.0,"elo_lo":1286.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1377.0,"imp":12.45,"score":0.128,"rank":32.79,"hrank":26.34,"elo_hi":1448.0,"elo_lo":1316.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1373.0,"imp":13.982,"score":0.159,"rank":33.01,"hrank":27.27,"elo_hi":1465.0,"elo_lo":1272.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1372.0,"imp":12.907,"score":0.137,"rank":33.13,"hrank":21.73,"elo_hi":1461.0,"elo_lo":1269.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","reference":false,"elo":1344.0,"imp":13.337,"score":0.157,"rank":34.99,"hrank":24.16,"elo_hi":1462.0,"elo_lo":1242.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1332.0,"imp":15.311,"score":0.174,"rank":35.84,"hrank":16.46,"elo_hi":1451.0,"elo_lo":1198.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1326.0,"imp":13.982,"score":0.096,"rank":36.23,"hrank":29.85,"elo_hi":1436.0,"elo_lo":1238.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1326.0,"imp":15.595,"score":0.134,"rank":36.24,"hrank":27.26,"elo_hi":1439.0,"elo_lo":1198.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1298.0,"imp":14.33,"score":0.073,"rank":38.1,"hrank":33.85,"elo_hi":1370.0,"elo_lo":1219.0,"imputed_pct":0.0,"imputed":false},{"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","reference":false,"elo":1279.0,"imp":16.418,"score":0.17,"rank":39.33,"hrank":9.92,"elo_hi":1419.0,"elo_lo":1119.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1272.0,"imp":14.114,"score":0.049,"rank":39.78,"hrank":35.01,"elo_hi":1342.0,"elo_lo":1197.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1272.0,"imp":15.091,"score":0.034,"rank":39.84,"hrank":36.58,"elo_hi":1345.0,"elo_lo":1204.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1265.0,"imp":16.441,"score":0.089,"rank":40.24,"hrank":32.83,"elo_hi":1369.0,"elo_lo":1132.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1258.0,"imp":14.82,"score":0.028,"rank":40.73,"hrank":38.32,"elo_hi":1309.0,"elo_lo":1194.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1244.0,"imp":15.1,"score":0.03,"rank":41.64,"hrank":35.97,"elo_hi":1329.0,"elo_lo":1159.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1221.0,"imp":17.87,"score":0.057,"rank":43.14,"hrank":37.72,"elo_hi":1309.0,"elo_lo":1085.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1217.0,"imp":15.304,"score":0.022,"rank":43.4,"hrank":40.85,"elo_hi":1296.0,"elo_lo":1128.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1214.0,"imp":15.744,"score":0.031,"rank":43.54,"hrank":37.85,"elo_hi":1300.0,"elo_lo":1130.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1201.0,"imp":17.192,"score":0.06,"rank":44.39,"hrank":36.49,"elo_hi":1311.0,"elo_lo":1063.0,"imputed_pct":0.0,"imputed":false},{"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","reference":false,"elo":1201.0,"imp":16.933,"score":0.028,"rank":44.41,"hrank":39.08,"elo_hi":1283.0,"elo_lo":1106.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1179.0,"imp":18.488,"score":0.033,"rank":45.73,"hrank":41.33,"elo_hi":1263.0,"elo_lo":1068.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1174.0,"imp":16.303,"score":0.008,"rank":46.07,"hrank":43.76,"elo_hi":1244.0,"elo_lo":1091.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1126.0,"imp":18.145,"score":0.001,"rank":48.9,"hrank":47.16,"elo_hi":1185.0,"elo_lo":1054.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1110.0,"imp":21.633,"score":0.019,"rank":49.83,"hrank":43.02,"elo_hi":1211.0,"elo_lo":985.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1101.0,"imp":19.294,"score":0.015,"rank":50.32,"hrank":45.23,"elo_hi":1192.0,"elo_lo":996.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1060.0,"imp":22.747,"score":0.004,"rank":52.57,"hrank":48.1,"elo_hi":1152.0,"elo_lo":944.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1046.0,"imp":25.98,"score":0.018,"rank":53.27,"hrank":47.13,"elo_hi":1193.0,"elo_lo":862.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1031.0,"imp":22.864,"score":0.018,"rank":54.04,"hrank":48.76,"elo_hi":1167.0,"elo_lo":860.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1000.0,"imp":21.197,"score":0.0,"rank":55.55,"hrank":54.46,"elo_hi":1051.0,"elo_lo":908.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":989.0,"imp":24.353,"score":0.005,"rank":56.06,"hrank":49.75,"elo_hi":1083.0,"elo_lo":861.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":988.0,"imp":25.807,"score":0.0,"rank":56.13,"hrank":54.65,"elo_hi":1044.0,"elo_lo":898.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":987.0,"imp":25.781,"score":0.0,"rank":56.18,"hrank":54.79,"elo_hi":1048.0,"elo_lo":901.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":976.0,"imp":22.789,"score":0.0,"rank":56.64,"hrank":54.71,"elo_hi":1070.0,"elo_lo":855.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":935.0,"imp":32.872,"score":0.004,"rank":58.45,"hrank":53.87,"elo_hi":1088.0,"elo_lo":709.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":901.0,"imp":29.424,"score":0.0,"rank":59.81,"hrank":57.82,"elo_hi":1007.0,"elo_lo":748.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":892.0,"imp":33.73,"score":0.002,"rank":60.16,"hrank":56.48,"elo_hi":1046.0,"elo_lo":647.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":848.0,"imp":30.685,"score":0.0,"rank":61.7,"hrank":61.11,"elo_hi":898.0,"elo_lo":760.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":814.0,"imp":29.973,"score":0.0,"rank":62.78,"hrank":62.5,"elo_hi":867.0,"elo_lo":708.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":797.0,"imp":35.794,"score":0.0,"rank":63.27,"hrank":61.49,"elo_hi":934.0,"elo_lo":553.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":593.0,"imp":43.501,"score":0.0,"rank":67.65,"hrank":67.52,"elo_hi":676.0,"elo_lo":447.0,"imputed_pct":0.0,"imputed":false}];
344
-
345
-
346
- const NS = "http://www.w3.org/2000/svg";
347
- // Baseline and Other are one bucket, as in the site's own type legend: they
348
- // already share a color, and each holds only a handful of methods.
349
- const FAM_MERGED = "Baseline / Other";
350
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
351
- // Normalized up front so every later lookup — colors, chips, sorting — sees
352
- // the merged family. Both templates declare POINTS above this block.
353
- for (const p of POINTS) p.family = famOf(p.family);
354
-
355
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
356
- const FAM_VAR = {
357
- "Foundation Model": "var(--fam-foundation)",
358
- "Tree-based": "var(--fam-tree)",
359
- "Neural Network": "var(--fam-nn)",
360
- "Reference Pipeline": "var(--fam-reference)",
361
- [FAM_MERGED]: "var(--fam-baseline)",
362
- };
363
- // The symbol the website shows for each family, so a family chip here reads the
364
- // same as the Type column on the site. Baseline and Other are one bucket, so
365
- // that chip carries both symbols.
366
- const FAM_SYMBOL = {
367
- "Foundation Model": "🧠⚡",
368
- "Tree-based": "🌳",
369
- "Neural Network": "🧠🔁",
370
- "Reference Pipeline": "📊",
371
- [FAM_MERGED]: "📏 ❓",
372
- };
373
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
374
- const VARIANT_VAR = {
375
- "Default": "var(--var-default)",
376
- "Tuned": "var(--var-tuned)",
377
- "Tuned + Ens.": "var(--var-tunedens)",
378
- };
379
-
380
- // A family chip's label: its symbol, its name and how many methods it holds.
381
- function famChipLabel(family, count) {
382
- const symbol = FAM_SYMBOL[family];
383
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
384
- family + ' <span class="count">&times;' + count + "</span>";
385
- }
386
-
387
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
388
- const FAM_INK = {
389
- "Foundation Model": "var(--fam-foundation-ink)",
390
- "Tree-based": "var(--fam-tree-ink)",
391
- "Neural Network": "var(--fam-nn-ink)",
392
- "Reference Pipeline": "var(--fam-reference-ink)",
393
- [FAM_MERGED]: "var(--fam-baseline-ink)",
394
- };
395
-
396
- // Create an SVG element with attributes, optionally appended to `parent`.
397
- function el(name, attrs, parent) {
398
- const node = document.createElementNS(NS, name);
399
- for (const k in attrs) node.setAttribute(k, attrs[k]);
400
- if (parent) parent.appendChild(node);
401
- return node;
402
- }
403
-
404
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
405
- // locale-independent by definition, which is the point: `toLocaleString`
406
- // would follow the *viewer's* browser locale and print 1234,5 for a German
407
- // visitor, disagreeing with the figures and CSVs beside it.
408
- function fmtNum(v, decimals) {
409
- if (v == null || !isFinite(v)) return "—";
410
- return v.toFixed(decimals);
411
- }
412
-
413
- function fmtMetric(metric, v) {
414
- if (v == null || !isFinite(v)) return "—";
415
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
416
- }
417
-
418
- function fmtTime(v) {
419
- if (v >= 100) return fmtNum(v, 0) + " s";
420
- if (v >= 1) return fmtNum(v, 1) + " s";
421
- if (v >= 0.1) return fmtNum(v, 2) + " s";
422
- return fmtNum(v, 3) + " s";
423
- }
424
-
425
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
426
- function niceStep(raw) {
427
- if (!(raw > 0)) return 1;
428
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
429
- for (const m of [1, 2, 2.5, 5, 10]) {
430
- if (mag * m >= raw) return mag * m;
431
- }
432
- return mag * 10;
433
- }
434
-
435
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
436
- // index rather than by accumulation so fractional steps do not drift.
437
- function ticks(min, max, target) {
438
- const step = niceStep((max - min) / target);
439
- const first = Math.ceil(min / step);
440
- const out = [];
441
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
442
- return out.length ? out : [min];
443
- }
444
-
445
- // A hover tooltip anchored inside `box` (which must be position:relative).
446
- function makeTooltip(box) {
447
- const node = box.querySelector(".tooltip");
448
- return {
449
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
450
- move(ev) {
451
- const r = box.getBoundingClientRect();
452
- let tx = ev.clientX - r.left + 14;
453
- const ty = ev.clientY - r.top + 12;
454
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
455
- node.style.left = tx + "px";
456
- node.style.top = ty + "px";
457
- },
458
- hide() { node.style.display = "none"; },
459
- };
460
- }
461
-
462
- // Paper view — white surface, legend + chart only — is the *default*: what a
463
- // reader wants first is the figure, and it is the state worth exporting. The
464
- // controls, chip list and data table are one click away behind "Edit view".
465
- // `afterToggle` re-renders charts whose size is measured from the layout.
466
- // `options.openInPaper` (default true) decides the state the page opens in. A
467
- // chart opens as the figure; the leaderboard table opens with its controls,
468
- // since there the interaction is the point rather than scaffolding around it.
469
- function setUpPaperView(afterToggle, options) {
470
- const opts = options || {};
471
- const root = document.documentElement;
472
- let hostTheme = null; // the embedding page's choice, captured on entry
473
- const btn = document.getElementById("btn-paper");
474
- const embedded = window.parent !== window;
475
-
476
- function setPaper(on) {
477
- document.body.classList.toggle("paper", on);
478
- if (on) {
479
- hostTheme = root.getAttribute("data-theme");
480
- root.setAttribute("data-theme", "light");
481
- } else if (hostTheme) {
482
- root.setAttribute("data-theme", hostTheme);
483
- } else {
484
- root.removeAttribute("data-theme");
485
- }
486
- btn.textContent = on ? "Edit view" : "Paper view";
487
- document.getElementById("exportbar").hidden = !on || embedded;
488
- if (afterToggle) requestAnimationFrame(afterToggle);
489
- postHeight();
490
- }
491
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
492
- // Embedded, the host page owns these controls — they sit beside the panel's
493
- // static-figure toggle and are driven from the outside. Standalone (the
494
- // shareable single file) this page needs its own.
495
- if (embedded) document.querySelector(".viewbar").hidden = true;
496
- window.addEventListener("message", ev => {
497
- const d = ev.data;
498
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
499
- });
500
- // Only standalone: embedded, the host owns the button and would not see the
501
- // key press, so its label would fall out of step with the frame.
502
- if (!embedded) {
503
- document.addEventListener("keydown", ev => {
504
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
505
- });
506
- }
507
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
508
- }
509
-
510
- // --- Figure export ---------------------------------------------------------
511
- // The chart is live SVG, so a file can be built from it directly. Three things
512
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
513
- // nothing outside this document), it has no background or font of its own, and
514
- // the legend is HTML rather than part of the SVG.
515
-
516
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
517
- // cloned and its label re-emitted at the measured position. foreignObject would
518
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
519
- // break the PNG path.
520
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
521
- // once the node leaves this document.
522
- function resolveVars(root, resolve) {
523
- for (const node of [root, ...root.querySelectorAll("*")]) {
524
- for (const attr of ["fill", "stroke"]) {
525
- const value = node.getAttribute(attr);
526
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
527
- }
528
- }
529
- }
530
-
531
- function legendToSvg(container, resolve) {
532
- const base = container.getBoundingClientRect();
533
- const group = document.createElementNS(NS, "g");
534
- let height = 0;
535
- for (const item of container.querySelectorAll(".item")) {
536
- const box = item.getBoundingClientRect();
537
- if (!box.width) continue;
538
- height = Math.max(height, box.bottom - base.top);
539
- let textLeft = box.left - base.left;
540
- const glyph = item.querySelector("svg");
541
- if (glyph) {
542
- const gbox = glyph.getBoundingClientRect();
543
- const wrap = el("g", {
544
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
545
- }, group);
546
- const glyphClone = glyph.cloneNode(true);
547
- resolveVars(glyphClone, resolve);
548
- wrap.appendChild(glyphClone);
549
- textLeft = gbox.right - base.left + 5;
550
- }
551
- const label = item.textContent.trim();
552
- if (!label) continue;
553
- const colored = item.querySelector("[style*='color']");
554
- const text = el("text", {
555
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
556
- fill: resolve(getComputedStyle(colored || item).color),
557
- }, group);
558
- text.textContent = label;
559
- }
560
- return { group, height: Math.ceil(height) };
561
- }
562
-
563
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
564
- // y-axis beside the scrolling plot) still exports as one figure.
565
- function buildExportSvg(parts, legendEl, pad = 10) {
566
- const rootStyle = getComputedStyle(document.documentElement);
567
- const resolve = value => String(value).replace(
568
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
569
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
570
-
571
- let chartW = 0, chartH = 0;
572
- for (const part of parts) {
573
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
574
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
575
- }
576
-
577
- const out = document.createElementNS(NS, "svg");
578
- out.setAttribute("xmlns", NS);
579
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
580
- let top = pad;
581
- const later = []; // built after the width is known
582
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
583
- if (legend && legend.height) {
584
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
585
- later.push(() => out.appendChild(legend.group));
586
- top += legend.height + 8;
587
- }
588
-
589
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
590
- const height = top + chartH + pad;
591
- out.setAttribute("width", Math.ceil(width));
592
- out.setAttribute("height", Math.ceil(height));
593
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
594
- for (const build of later) build();
595
-
596
- for (const part of parts) {
597
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
598
- const clone = part.svg.cloneNode(true);
599
- resolveVars(clone, resolve);
600
- while (clone.firstChild) group.appendChild(clone.firstChild);
601
- }
602
- return out;
603
- }
604
-
605
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
606
- function slugify(text) {
607
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
608
- }
609
-
610
- function downloadUrl(url, filename) {
611
- const a = document.createElement("a");
612
- a.href = url;
613
- a.download = filename;
614
- document.body.appendChild(a);
615
- a.click();
616
- a.remove();
617
- }
618
-
619
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
620
- function rasterize(svg, scale, done, fail) {
621
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
622
- const img = new Image();
623
- img.onload = () => {
624
- const canvas = document.createElement("canvas");
625
- canvas.width = Math.round(width * scale);
626
- canvas.height = Math.round(height * scale);
627
- const ctx = canvas.getContext("2d");
628
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
629
- ctx.drawImage(img, 0, 0);
630
- done(canvas, width, height);
631
- };
632
- img.onerror = fail;
633
- img.src = "data:image/svg+xml;charset=utf-8,"
634
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
635
- }
636
-
637
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
638
- // cost this page its zero-dependency, single-file property. The image is stored
639
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
640
- // in points to the figure's CSS size, so it prints at the size it appears here
641
- // and the pixels land at 96*scale dpi.
642
- async function buildPdf(canvas, cssWidth, cssHeight) {
643
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
644
- const rgb = new Uint8Array((pixels.length / 4) * 3);
645
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
646
- rgb[j] = pixels[i];
647
- rgb[j + 1] = pixels[i + 1];
648
- rgb[j + 2] = pixels[i + 2];
649
- }
650
- const deflated = new Uint8Array(await new Response(
651
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
652
-
653
- const encoder = new TextEncoder();
654
- const chunks = [];
655
- const offsets = [];
656
- let cursor = 0;
657
- const put = data => {
658
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
659
- chunks.push(bytes);
660
- cursor += bytes.length;
661
- };
662
- const object = (id, body, stream) => {
663
- offsets[id] = cursor;
664
- put(`${id} 0 obj\n${body}\n`);
665
- if (stream) {
666
- put("stream\n");
667
- put(stream);
668
- put("\nendstream\n");
669
- }
670
- put("endobj\n");
671
- };
672
-
673
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
674
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
675
- put("%PDF-1.4\n");
676
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
677
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
678
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
679
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
680
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
681
- object(4, "<< /Type /XObject /Subtype /Image "
682
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
683
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
684
- object(5, `<< /Length ${content.length} >>`, content);
685
-
686
- const xref = cursor;
687
- let table = "xref\n0 6\n0000000000 65535 f \n";
688
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
689
- put(table);
690
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
691
- return new Blob(chunks, { type: "application/pdf" });
692
- }
693
-
694
- // Wire up the export buttons; `getParts` is called per click so the file always
695
- // matches what is on screen. Returns a `run(format)` the host page can drive.
696
- function setUpExport(getParts, basename) {
697
- const buttons = {
698
- svg: document.getElementById("btn-svg"),
699
- png: document.getElementById("btn-png"),
700
- pdf: document.getElementById("btn-pdf"),
701
- };
702
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
703
-
704
- // A sandboxed frame has no modals, so a failure is reported on the button.
705
- function complain(format) {
706
- const button = buttons[format];
707
- if (!button) return;
708
- const label = button.textContent;
709
- button.textContent = "failed";
710
- setTimeout(() => { button.textContent = label; }, 2500);
711
- }
712
-
713
- function run(format) {
714
- const svg = figure();
715
- const name = basename();
716
- if (format === "svg") {
717
- downloadUrl("data:image/svg+xml;charset=utf-8,"
718
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
719
- return;
720
- }
721
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
722
- // points so the pixels already land near 200 dpi at print size.
723
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
724
- if (format === "png") {
725
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
726
- } else {
727
- buildPdf(canvas, cssWidth, cssHeight)
728
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
729
- .catch(() => complain("pdf"));
730
- }
731
- }, () => complain(format));
732
- }
733
-
734
- for (const format of Object.keys(buttons)) {
735
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
736
- }
737
- // Embedded, the buttons live in the host's panel header (see main.taExport).
738
- window.addEventListener("message", ev => {
739
- const d = ev.data;
740
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
741
- });
742
- }
743
-
744
- // When embedded, report the content height so the host page can size the
745
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
746
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
747
- // clamped to at least the iframe's current viewport, which turns the
748
- // resize round-trip into a grow-forever feedback loop. The change guard
749
- // stops re-posting once the height settles.
750
- let lastPostedHeight = 0;
751
- function postHeight() {
752
- if (window.parent === window) return;
753
- const height = Math.ceil(document.body.offsetHeight);
754
- if (Math.abs(height - lastPostedHeight) < 3) return;
755
- lastPostedHeight = height;
756
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
757
- }
758
-
759
-
760
- // ---------- geometry ----------
761
- const AXIS_W = 72; // width of the sticky y-axis pane
762
- const PLOT_H = 320; // height of the plot area itself
763
- const TOP = 14; // headroom above the tallest bar
764
- const LABEL_TOP = 18; // axis line -> first label row
765
- const LABEL_ROW = 19; // vertical offset of the staggered second row
766
- const LABEL_SIZE = 14; // method names; the slot below scales with it
767
- const TICK_SIZE = 12.5;
768
- // Method names are set horizontally (rotated ones are markedly harder to
769
- // read), staggered over two rows exactly like the static figure — so a slot
770
- // has to be wide enough for half a name. Past ~24 methods the chart scrolls
771
- // rather than squeezing every column into the viewport.
772
- // No upper bound on the slot: with few methods selected a capped slot left
773
- // the columns huddled on the left with dead space beside them, so the slots
774
- // simply share out whatever width there is.
775
- const MIN_SLOT = 66;
776
- const BAR_FRAC = 0.88; // share of the slot the widest bar takes
777
- const MAX_BAR = 70; // ...but never wider than this, however few columns
778
-
779
- // Variant -> (color token, width relative to the widest bar). The bars are
780
- // concentric, so the nesting itself encodes the tuning progression.
781
- const VARIANT_STYLE = {
782
- "Tuned + Ens.": { color: "var(--var-tunedens)", rel: 1 },
783
- "Tuned": { color: "var(--var-tuned)", rel: 0.8 },
784
- "Default": { color: "var(--var-default)", rel: 0.6 },
785
- };
786
- const VARIANT_ORDER = ["Default", "Tuned", "Tuned + Ens."];
787
- // Dash patterns cycle so several reference lines stay distinguishable.
788
- const REF_DASHES = ["8 5", "2 4", "12 4 3 4"];
789
-
790
- const titleEl = document.getElementById("title");
791
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
792
-
793
- const axisSvg = document.getElementById("axis");
794
- const svg = document.getElementById("chart");
795
- const scroller = document.getElementById("scroller");
796
- const wrap = document.getElementById("chartwrap");
797
- const yminRange = document.getElementById("ymin-range");
798
- const yminVal = document.getElementById("ymin-val");
799
- const tip = makeTooltip(wrap);
800
-
801
- // ---------- data ----------
802
- const METRICS = CONFIG.metrics;
803
- const metricByKey = {};
804
- for (const m of METRICS) metricByKey[m.key] = m;
805
-
806
- // One entry per method (its variants grouped); reference pipelines are kept
807
- // apart — they are drawn as threshold lines, not as columns.
808
- const byMethod = new Map();
809
- const refs = [];
810
- for (const p of POINTS) {
811
- if (p.reference) { refs.push(p); continue; }
812
- let entry = byMethod.get(p.method);
813
- if (!entry) {
814
- entry = { method: p.method, family: p.family, url: p.url, points: [] };
815
- byMethod.set(p.method, entry);
816
- }
817
- entry.points.push(p);
818
- }
819
- for (const e of byMethod.values()) {
820
- e.points.sort((a, b) => VARIANT_ORDER.indexOf(a.variant) - VARIANT_ORDER.indexOf(b.variant));
821
- e.imputed = e.points.some(p => p.imputed);
822
- e.imputed_pct = Math.max(...e.points.map(p => p.imputed_pct || 0));
823
- }
824
-
825
- const state = {
826
- metric: METRICS[0].key,
827
- sort: "best",
828
- yMin: null, // null = the automatic axis floor; a number = zoomed in
829
-
830
- methods: new Set(byMethod.keys()),
831
- refs: new Set(refs.map(r => r.method)),
832
- variants: new Set(VARIANT_ORDER),
833
- };
834
-
835
- function metric() { return metricByKey[state.metric]; }
836
-
837
- // A method's best value under `m`, ignoring variants the reader switched off.
838
- function bestOf(entry, m) {
839
- const vals = entry.points
840
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
841
- .map(p => p[m.key]);
842
- if (!vals.length) return null;
843
- return m.lowerBetter ? Math.min(...vals) : Math.max(...vals);
844
- }
845
-
846
- function sortedMethods(entries, m) {
847
- const arr = [...entries];
848
- const cmp = {
849
- best: (a, b) => rankVal(a, m) - rankVal(b, m),
850
- worst: (a, b) => rankVal(b, m) - rankVal(a, m),
851
- name: (a, b) => a.method.localeCompare(b.method),
852
- family: (a, b) =>
853
- FAM_ORDER.indexOf(a.family) - FAM_ORDER.indexOf(b.family) || rankVal(a, m) - rankVal(b, m),
854
- }[state.sort];
855
- return arr.sort(cmp);
856
- }
857
- // Sort key that puts "better" first for either metric direction, with
858
- // value-less methods last.
859
- function rankVal(entry, m) {
860
- const v = bestOf(entry, m);
861
- if (v == null) return Infinity;
862
- return m.lowerBetter ? v : -v;
863
- }
864
-
865
- function visibleEntries() {
866
- const m = metric();
867
- return sortedMethods([...byMethod.values()].filter(e => state.methods.has(e.method) && bestOf(e, m) != null), m);
868
- }
869
- function visibleRefs() {
870
- return refs.filter(r => state.refs.has(r.method) && r[state.metric] != null);
871
- }
872
-
873
- // Rendered width of each label, measured in the live document (font metrics
874
- // are not knowable up front): it decides whether the names fit on one row or
875
- // need the two-row stagger, and which ones have to be shortened.
876
- function measureLabels(names) {
877
- const probe = el("g", { visibility: "hidden" }, svg);
878
- const widths = names.map(name => {
879
- const t = el("text", { "font-size": LABEL_SIZE }, probe);
880
- t.textContent = name;
881
- return t.getComputedTextLength();
882
- });
883
- probe.remove();
884
- return widths;
885
- }
886
-
887
- // Trim a name to `budget` px, ending in an ellipsis. The full name stays one
888
- // hover (and one data-table row) away.
889
- function fitLabel(node, name, width, budget) {
890
- if (width <= budget) return;
891
- let text = name;
892
- while (text.length > 1 && node.getComputedTextLength() > budget) {
893
- text = text.slice(0, -1);
894
- node.textContent = text + "…";
895
- }
896
- }
897
-
898
- // ---------- chart ----------
899
- function render() {
900
- const m = metric();
901
- const entries = visibleEntries();
902
- const shownRefs = visibleRefs();
903
- svg.textContent = "";
904
- axisSvg.textContent = "";
905
- axisSvg.setAttribute("width", AXIS_W);
906
-
907
- const avail = Math.max(240, scroller.clientWidth - 2);
908
- if (!entries.length) {
909
- axisSvg.setAttribute("height", 120);
910
- svg.setAttribute("width", avail);
911
- svg.setAttribute("height", 120);
912
- const t = el("text", { x: avail / 2, y: 60, "text-anchor": "middle", "font-size": 13, fill: "var(--muted)" }, svg);
913
- t.textContent = "No methods selected — use “All methods” to bring them back.";
914
- buildLegend(m, shownRefs);
915
- postHeight();
916
- return;
917
- }
918
-
919
- const slot = Math.max(MIN_SLOT, avail / entries.length);
920
- const barUnit = Math.min(MAX_BAR, slot * BAR_FRAC);
921
- const plotW = Math.max(avail, slot * entries.length);
922
- // Names go on one row when they fit side by side, otherwise on two
923
- // staggered rows (each label then has two slots of room).
924
- const labels = entries.map(e => e.method + (e.imputed ? " ‡" : ""));
925
- const labelWidths = measureLabels(labels);
926
- const widest = Math.max(...labelWidths);
927
- const labelRows = widest <= slot - 6 ? 1 : 2;
928
- const H = TOP + PLOT_H + LABEL_TOP + (labelRows - 1) * LABEL_ROW + 10;
929
- svg.setAttribute("width", plotW);
930
- svg.setAttribute("height", H);
931
- axisSvg.setAttribute("height", H);
932
-
933
- // -- y domain: from zero where the metric has one, else a floor below the
934
- // shortest bar (Elo has no meaningful zero, and starting at zero would
935
- // squash every difference into the top fifth of the chart). The floor
936
- // ignores the CI whiskers so one wide interval cannot deflate the scale.
937
- const barVals = [];
938
- const allVals = [];
939
- for (const e of entries) {
940
- for (const p of e.points) {
941
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
942
- barVals.push(p[m.key]);
943
- allVals.push(p[m.key]);
944
- if (m.ci && p[m.ci.hi] != null) allVals.push(p[m.ci.hi]);
945
- }
946
- }
947
- for (const r of shownRefs) { barVals.push(r[state.metric]); allVals.push(r[state.metric]); }
948
- const barMin = Math.min(...barVals), barMax = Math.max(...allVals);
949
- const span = barMax - barMin || Math.abs(barMax) || 1;
950
- let autoY0, y1;
951
- if (m.fromZero) {
952
- autoY0 = 0;
953
- y1 = barMax * 1.06 || 1;
954
- } else {
955
- // Snap the floor down to a tick multiple so the axis reads in round
956
- // numbers, and keep it just below the shortest bar: every bar stays
957
- // visible (the static figure clips the ones below its fixed floor).
958
- const step = niceStep(span / 6);
959
- autoY0 = Math.floor((barMin - span * 0.05) / step) * step;
960
- y1 = barMax + span * 0.04;
961
- }
962
- // The zoom slider raises the floor from there toward the top of the field,
963
- // magnifying the differences between the leaders. Its bounds follow the
964
- // metric's own scale, so they are refreshed on every render; the bars and
965
- // whiskers that drop below the new floor are clipped away (see plot-clip).
966
- const zoomMax = autoY0 + (y1 - autoY0) * 0.9;
967
- yminRange.min = autoY0;
968
- yminRange.max = zoomMax;
969
- yminRange.step = (zoomMax - autoY0) / 200 || 1;
970
- if (state.yMin != null) state.yMin = Math.min(state.yMin, zoomMax);
971
- if (state.yMin != null && state.yMin <= autoY0) state.yMin = null;
972
- const y0 = state.yMin == null ? autoY0 : state.yMin;
973
- yminRange.value = y0;
974
- yminVal.textContent = state.yMin == null ? "auto" : fmtNum(y0, m.decimals);
975
- const Y = v => TOP + (1 - (v - y0) / (y1 - y0)) * PLOT_H;
976
- const baseY = Y(y0);
977
-
978
- // -- grid (solid hairlines) in the plot pane, tick labels in the axis pane
979
- const tickVals = ticks(y0, y1, 6);
980
- const grid = el("g", {}, svg);
981
- const tickLabels = [];
982
- for (const tv of tickVals) {
983
- const gy = Y(tv);
984
- el("line", { x1: 0, y1: gy, x2: plotW, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
985
- const t = el("text", {
986
- x: AXIS_W - 10, y: gy + 4, "text-anchor": "end", "font-size": TICK_SIZE, fill: "var(--muted)",
987
- }, axisSvg);
988
- t.textContent = fmtNum(tv, m.decimals);
989
- tickLabels.push({ y: gy, node: t });
990
- }
991
- el("line", { x1: 0, y1: baseY, x2: plotW, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, grid);
992
- el("line", { x1: AXIS_W - 1, y1: TOP, x2: AXIS_W - 1, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, axisSvg);
993
- el("text", {
994
- x: 0, y: 0, "text-anchor": "middle", "font-size": LABEL_SIZE, "font-weight": 650,
995
- fill: "var(--ink)", transform: `translate(15 ${TOP + PLOT_H / 2}) rotate(-90)`,
996
- }, axisSvg).textContent = m.axisLabel;
997
-
998
- // -- hover band behind the marks (highlights the whole column group)
999
- const band = el("rect", {
1000
- x: 0, y: TOP, width: slot, height: PLOT_H + 6, fill: "var(--ink)", opacity: 0, "pointer-events": "none",
1001
- }, svg);
1002
-
1003
- const defs = el("defs", {}, svg);
1004
- // -- imputed hatch, reused by every partially imputed bar. Crossed diagonals,
1005
- // matching the "x" hatch matplotlib draws in the static figure; the
1006
- // diagonals meet the tile corners exactly, so the grid tiles seamlessly.
1007
- const pat = el("pattern", {
1008
- id: "imp-hatch", width: 8, height: 8, patternUnits: "userSpaceOnUse",
1009
- }, defs);
1010
- el("path", {
1011
- d: "M0,0 L8,8 M8,0 L0,8", stroke: "var(--paper)", "stroke-width": 1.5,
1012
- opacity: 0.85, fill: "none",
1013
- }, pat);
1014
-
1015
- // -- Everything data-bearing is clipped to the plot area. The axis floor is
1016
- // set from the bar values (a couple of very wide intervals would
1017
- // otherwise deflate the whole scale), so a long lower CI whisker can
1018
- // reach past it and run into the method names below. Clip it there; the
1019
- // exact interval stays in the tooltip and the data table.
1020
- const clip = el("clipPath", { id: "plot-clip" }, defs);
1021
- el("rect", { x: 0, y: 0, width: plotW, height: baseY }, clip);
1022
-
1023
- // -- bars: one concentric group per method
1024
- const barsG = el("g", { "clip-path": "url(#plot-clip)" }, svg);
1025
- entries.forEach((entry, i) => {
1026
- const cx = i * slot + slot / 2;
1027
- // Widest bar first, narrowest last: the bars are concentric, so painting
1028
- // them by width guarantees each one stays visible. Ordering by height
1029
- // instead loses a variant outright whenever a narrower bar is the taller
1030
- // of the two (TabSTAR's tuned bar sat 1 Elo above tuned + ensembled, and
1031
- // the wider bar covered it completely).
1032
- const relOf = p => (VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"]).rel;
1033
- const drawn = entry.points
1034
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
1035
- .slice()
1036
- .sort((a, b) => relOf(b) - relOf(a));
1037
- for (const p of drawn) {
1038
- const style = VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"];
1039
- const w = barUnit * style.rel;
1040
- const top = Y(p[m.key]);
1041
- const rect = {
1042
- x: cx - w / 2, y: Math.min(top, baseY), width: w, height: Math.max(1, Math.abs(baseY - top)),
1043
- };
1044
- // 2px surface ring, not a border: it is what keeps a nested bar legible
1045
- // against the wider bar it sits inside.
1046
- el("rect", { ...rect, fill: style.color, stroke: "var(--paper)", "stroke-width": 1.5, rx: 2 }, barsG);
1047
- if (p.imputed) el("rect", { ...rect, fill: "url(#imp-hatch)", rx: 2 }, barsG);
1048
- if (m.ci && p[m.ci.lo] != null) {
1049
- const cap = Math.max(2.5, w * 0.34);
1050
- const whisk = el("g", {
1051
- stroke: `color-mix(in srgb, ${style.color} 55%, var(--ink))`, "stroke-width": 1.4, opacity: 0.9,
1052
- }, barsG);
1053
- el("line", { x1: cx, y1: Y(p[m.ci.lo]), x2: cx, y2: Y(p[m.ci.hi]) }, whisk);
1054
- el("line", { x1: cx - cap, y1: Y(p[m.ci.hi]), x2: cx + cap, y2: Y(p[m.ci.hi]) }, whisk);
1055
- el("line", { x1: cx - cap, y1: Y(p[m.ci.lo]), x2: cx + cap, y2: Y(p[m.ci.lo]) }, whisk);
1056
- }
1057
- }
1058
- });
1059
-
1060
- // -- x axis: the method name, colored by model family (the legend below
1061
- // names the colors).
1062
- const xg = el("g", {}, svg);
1063
- // Hairlines share one group that is painted before every name, so a name
1064
- // nudged sideways at the edge (see below) passes over a neighbouring
1065
- // column's hairline instead of being crossed out by it.
1066
- const xLines = el("g", {}, xg);
1067
- const xLabels = el("g", {}, xg);
1068
- entries.forEach((entry, i) => {
1069
- const cx = i * slot + slot / 2;
1070
- const row = labelRows === 1 ? 0 : i % 2;
1071
- const y = baseY + LABEL_TOP + row * LABEL_ROW;
1072
- // A hairline drops the staggered row back to its own column.
1073
- if (row) {
1074
- el("line", {
1075
- x1: cx, y1: baseY + 3, x2: cx, y2: y - 10,
1076
- stroke: "var(--line)", "stroke-width": 1,
1077
- }, xLines);
1078
- }
1079
- const t = el("text", {
1080
- x: cx, y, "font-size": LABEL_SIZE, "text-anchor": "middle", "font-weight": 650,
1081
- fill: FAM_INK[entry.family] || "var(--ink)",
1082
- // A halo in the surface color keeps the glyphs legible where a nudged
1083
- // name crosses a hairline, rather than the line running through them.
1084
- "paint-order": "stroke", stroke: "var(--paper)", "stroke-width": 3,
1085
- }, xLabels);
1086
- t.textContent = labels[i];
1087
- fitLabel(t, labels[i], labelWidths[i], slot * labelRows - 8);
1088
- // The outermost columns sit only half a slot from the edge, so a name
1089
- // wider than one slot would reach past the SVG viewport and be cut off
1090
- // there (a name may occupy two slots when the rows are staggered). Nudge
1091
- // it inwards by just enough to stay whole; every other label keeps its
1092
- // column centre, and the bar below still marks the column.
1093
- const half = t.getComputedTextLength() / 2;
1094
- t.setAttribute("x", Math.max(half + 1, Math.min(cx, plotW - half - 1)));
1095
- });
1096
-
1097
- // -- reference pipelines as threshold lines. Their names live in the legend
1098
- // (matched by dash pattern) rather than on the line, where they would
1099
- // cover the tallest bars at every scroll position.
1100
- const refG = el("g", {}, svg);
1101
- const tagYs = [];
1102
- shownRefs.forEach((r, i) => {
1103
- const ry = Y(r[state.metric]);
1104
- if (ry < TOP || ry > baseY) return;
1105
- el("line", {
1106
- x1: 0, y1: ry, x2: plotW, y2: ry, stroke: "var(--fam-reference)", "stroke-width": 1.8,
1107
- "stroke-dasharray": REF_DASHES[i % REF_DASHES.length], opacity: 0.95,
1108
- }, refG);
1109
- // Sticky value tag in the axis pane, so the threshold stays readable at
1110
- // any scroll position. Two nearby thresholds would print on top of each
1111
- // other, so nudge each tag clear of the ones already placed; a tag wins
1112
- // over a tick label it would sit on.
1113
- let ty = ry;
1114
- while (tagYs.some(y => Math.abs(y - ty) < 12)) ty += 12;
1115
- tagYs.push(ty);
1116
- for (const t of tickLabels) {
1117
- if (Math.abs(t.y - ty) < 10) t.node.remove();
1118
- }
1119
- el("text", {
1120
- x: AXIS_W - 10, y: ty + 4, "text-anchor": "end", "font-size": 11, "font-weight": 650,
1121
- fill: "var(--fam-reference)",
1122
- }, axisSvg).textContent = fmtMetric(m, r[state.metric]);
1123
- });
1124
- buildLegend(m, shownRefs);
1125
-
1126
- // -- hit targets: one full-height column per method (>= 34px wide)
1127
- const hits = el("g", {}, svg);
1128
- entries.forEach((entry, i) => {
1129
- const h = el("rect", {
1130
- x: i * slot, y: TOP, width: slot, height: PLOT_H + 6, fill: "transparent", cursor: "pointer",
1131
- }, hits);
1132
- h.addEventListener("mouseenter", ev => {
1133
- band.setAttribute("x", i * slot);
1134
- band.setAttribute("opacity", 0.06);
1135
- showTip(entry, ev);
1136
- });
1137
- h.addEventListener("mousemove", ev => tip.move(ev));
1138
- h.addEventListener("mouseleave", () => { band.setAttribute("opacity", 0); tip.hide(); });
1139
- h.addEventListener("click", () => toggleMethod(entry.method));
1140
- });
1141
-
1142
- postHeight();
1143
- }
1144
-
1145
- function showTip(entry, ev) {
1146
- const m = metric();
1147
- let html = `<div class="t-name">${entry.method}</div><div>${entry.family}</div>`;
1148
- for (const p of entry.points) {
1149
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
1150
- const ci = m.ci && p[m.ci.lo] != null
1151
- ? ` <span class="t-var">(${fmtNum(p[m.ci.lo], m.decimals)}–${fmtNum(p[m.ci.hi], m.decimals)})</span>`
1152
- : "";
1153
- html += `<div><span class="t-var">${p.variant}:</span> <b>${fmtMetric(m, p[m.key])}</b>${ci}</div>`;
1154
- }
1155
- if (entry.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(entry.imputed_pct, 0)}% of datasets</div>`;
1156
- tip.show(html, ev);
1157
- }
1158
-
1159
- // ---------- chips ----------
1160
- const chipsBox = document.getElementById("chips");
1161
- const chipByMethod = new Map();
1162
- const famChips = new Map();
1163
-
1164
- function familyMembers(fam) {
1165
- const out = [...byMethod.values()].filter(e => e.family === fam).map(e => e.method);
1166
- for (const r of refs) if (r.family === fam) out.push(r.method);
1167
- return out;
1168
- }
1169
- function isOn(name) { return state.methods.has(name) || state.refs.has(name); }
1170
-
1171
- function buildChips() {
1172
- const rankMetric = metricByKey[CONFIG.rankMetric] || METRICS[0];
1173
- const head = document.createElement("div");
1174
- head.className = "chips-head";
1175
- head.textContent = "Methods shown — click to remove, click a family to toggle the whole group";
1176
- chipsBox.appendChild(head);
1177
- for (const fam of FAM_ORDER) {
1178
- const members = familyMembers(fam);
1179
- if (!members.length) continue;
1180
- members.sort((a, b) => chipRank(a, rankMetric) - chipRank(b, rankMetric));
1181
- const row = document.createElement("div");
1182
- row.className = "chiprow";
1183
- const famBtn = document.createElement("button");
1184
- famBtn.className = "famchip";
1185
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1186
- famBtn.innerHTML = famChipLabel(fam, members.length);
1187
- famBtn.title = `Toggle all ${members.length} ${fam} methods`;
1188
- famBtn.addEventListener("click", () => toggleFamily(fam));
1189
- row.appendChild(famBtn);
1190
- famChips.set(fam, famBtn);
1191
- const set = document.createElement("div");
1192
- set.className = "chipset";
1193
- for (const name of members) {
1194
- const entry = byMethod.get(name);
1195
- const b = document.createElement("button");
1196
- b.className = "chip";
1197
- b.style.setProperty("--fam", FAM_VAR[fam]);
1198
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1199
- b.appendChild(Object.assign(document.createElement("span"), { textContent: name }));
1200
- if (entry && entry.imputed) {
1201
- const mark = document.createElement("span");
1202
- mark.className = "imp-mark";
1203
- mark.textContent = "‡";
1204
- b.appendChild(mark);
1205
- }
1206
- b.title = name + (entry && entry.imputed ? " — partially imputed" : "");
1207
- b.addEventListener("click", () => toggleMethod(name));
1208
- set.appendChild(b);
1209
- chipByMethod.set(name, b);
1210
- }
1211
- row.appendChild(set);
1212
- chipsBox.appendChild(row);
1213
- }
1214
- }
1215
- function chipRank(name, m) {
1216
- const entry = byMethod.get(name);
1217
- if (entry) return rankVal(entry, m);
1218
- const r = refs.find(x => x.method === name);
1219
- const v = r ? r[m.key] : null;
1220
- return v == null ? Infinity : (m.lowerBetter ? v : -v);
1221
- }
1222
- function syncChips() {
1223
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(isOn(name)));
1224
- for (const [fam, b] of famChips) b.setAttribute("aria-pressed", String(familyMembers(fam).every(isOn)));
1225
- }
1226
-
1227
- function toggleMethod(name) {
1228
- const set = byMethod.has(name) ? state.methods : state.refs;
1229
- if (set.has(name)) set.delete(name); else set.add(name);
1230
- syncChips();
1231
- render();
1232
- }
1233
- function toggleFamily(fam) {
1234
- const members = familyMembers(fam);
1235
- const allOn = members.every(isOn);
1236
- for (const name of members) {
1237
- const set = byMethod.has(name) ? state.methods : state.refs;
1238
- if (allOn) set.delete(name); else set.add(name);
1239
- }
1240
- syncChips();
1241
- render();
1242
- }
1243
- function setMethods(names) {
1244
- state.methods = new Set(names);
1245
- syncChips();
1246
- render();
1247
- }
1248
-
1249
- document.getElementById("btn-all").addEventListener("click", () => {
1250
- state.refs = new Set(refs.map(r => r.method));
1251
- setMethods(byMethod.keys());
1252
- });
1253
- document.getElementById("btn-none").addEventListener("click", () => {
1254
- state.refs = new Set();
1255
- setMethods([]);
1256
- });
1257
- document.getElementById("btn-top").addEventListener("click", () => {
1258
- const m = metric();
1259
- // Rank explicitly rather than reusing the display sort: which 15 methods are
1260
- // kept must not depend on which end of the axis the best ones are drawn at
1261
- // (sorting "best on the right" would otherwise select the 15 worst).
1262
- const top = [...byMethod.values()]
1263
- .sort((a, b) => rankVal(a, m) - rankVal(b, m))
1264
- .slice(0, 15)
1265
- .map(e => e.method);
1266
- state.refs = new Set(refs.map(r => r.method));
1267
- setMethods(top);
1268
- });
1269
-
1270
- // ---------- selectors ----------
1271
- yminRange.addEventListener("input", ev => {
1272
- state.yMin = Number(ev.target.value);
1273
- render();
1274
- });
1275
-
1276
- const metricSelect = document.getElementById("metric-select");
1277
- for (const m of METRICS) {
1278
- metricSelect.appendChild(Object.assign(document.createElement("option"), { value: m.key, textContent: m.label }));
1279
- }
1280
- metricSelect.addEventListener("change", ev => {
1281
- state.metric = ev.target.value;
1282
- state.yMin = null; // the previous floor means nothing on a new scale
1283
- buildTable();
1284
- render();
1285
- });
1286
-
1287
- const sortSelect = document.getElementById("sort-select");
1288
- for (const [value, label] of [
1289
- ["best", "Best on the left"], ["worst", "Best on the right"],
1290
- ["family", "Model family"], ["name", "A–Z"],
1291
- ]) {
1292
- sortSelect.appendChild(Object.assign(document.createElement("option"), { value, textContent: label }));
1293
- }
1294
- sortSelect.value = state.sort;
1295
- sortSelect.addEventListener("change", ev => { state.sort = ev.target.value; render(); });
1296
-
1297
- // Variant toggles: buttons rather than chips, since they filter the series
1298
- // rather than the rows. "Default" gets no button at all — every method has a
1299
- // default result, so it is the baseline of the chart rather than an option
1300
- // (switching it off would leave the default-only methods with no bar). Only
1301
- // the extras layered on top, tuning and ensembling, are toggleable; the legend
1302
- // still carries Default's color.
1303
- const ALWAYS_SHOWN = "Default";
1304
- const variantBtns = document.getElementById("variant-btns");
1305
- const variantBtnByKey = new Map();
1306
- for (const v of VARIANT_ORDER) {
1307
- if (v === ALWAYS_SHOWN || !POINTS.some(p => p.variant === v)) continue;
1308
- const b = document.createElement("button");
1309
- b.className = "btn";
1310
- b.textContent = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1311
- b.style.setProperty("--fam", VARIANT_STYLE[v].color);
1312
- b.title = `Show or hide the ${b.textContent.toLowerCase()} bars`;
1313
- b.addEventListener("click", () => {
1314
- if (state.variants.has(v)) state.variants.delete(v);
1315
- else state.variants.add(v);
1316
- syncVariantBtns();
1317
- render();
1318
- });
1319
- variantBtns.appendChild(b);
1320
- variantBtnByKey.set(v, b);
1321
- }
1322
- function syncVariantBtns() {
1323
- for (const [v, b] of variantBtnByKey) {
1324
- const on = state.variants.has(v);
1325
- b.setAttribute("aria-pressed", String(on));
1326
- b.style.opacity = on ? "1" : "0.45";
1327
- b.style.borderColor = on ? VARIANT_STYLE[v].color : "var(--line)";
1328
- }
1329
- }
1330
-
1331
- // ---------- legend ----------
1332
- // Rebuilt on every render: it names the reference lines (each by its dash
1333
- // pattern and current value), which change with the metric and the selection.
1334
- function buildLegend(m, shownRefs) {
1335
- const parts = [];
1336
- for (const v of VARIANT_ORDER) {
1337
- if (!POINTS.some(p => p.variant === v)) continue;
1338
- const label = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1339
- const off = state.variants.has(v) ? "" : ' style="opacity:0.4"';
1340
- parts.push(
1341
- `<span class="item"${off}><svg width="12" height="12" viewBox="0 0 12 12">` +
1342
- `<rect x="1" y="1" width="10" height="10" rx="2" fill="${VARIANT_STYLE[v].color}"/></svg> ${label}</span>`);
1343
- }
1344
- if (m.ci) {
1345
- parts.push('<span class="item"><svg width="12" height="14" viewBox="0 0 12 14">' +
1346
- '<path d="M6,2 V12 M2,2 H10 M2,12 H10" stroke="var(--muted)" stroke-width="1.4" fill="none"/></svg> 95% CI</span>');
1347
- }
1348
- shownRefs.forEach((r, i) => {
1349
- parts.push(`<span class="item"><svg width="26" height="8" viewBox="0 0 26 8">` +
1350
- `<line x1="0" y1="4" x2="26" y2="4" stroke="var(--fam-reference)" stroke-width="1.8" ` +
1351
- `stroke-dasharray="${REF_DASHES[i % REF_DASHES.length]}"/></svg> ${r.method} · ${fmtMetric(m, r[state.metric])}</span>`);
1352
- });
1353
- if (POINTS.some(p => p.imputed)) {
1354
- parts.push('<span class="item"><svg width="14" height="14" viewBox="0 0 14 14">' +
1355
- '<rect x="1" y="1" width="12" height="12" rx="2" fill="var(--pt-muted)"/>' +
1356
- '<path d="M1,1 L7,7 M7,1 L1,7 M7,7 L13,13 M13,7 L7,13 M1,7 L7,13 M7,7 L13,1" ' +
1357
- 'stroke="var(--paper)" stroke-width="1.5" fill="none"/>' +
1358
- "</svg> &Dagger; partially imputed</span>");
1359
- }
1360
- // Model family, named rather than merely pointed at: without the colors
1361
- // spelled out, the swatch under each column decodes to nothing.
1362
- const families = FAM_ORDER.filter(f => POINTS.some(p => !p.reference && p.family === f));
1363
- if (families.length > 1) {
1364
- parts.push('<span class="legendbreak"></span><span class="item">Family:</span>');
1365
- for (const fam of families) {
1366
- parts.push(`<span class="item"><svg width="16" height="9" viewBox="0 0 16 9">` +
1367
- `<rect x="0" y="1" width="16" height="7" rx="2" fill="${FAM_VAR[fam]}"/></svg> ` +
1368
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`);
1369
- }
1370
- }
1371
- document.getElementById("legendstrip").innerHTML = parts.join("");
1372
- }
1373
-
1374
- // ---------- data table (the WCAG-clean twin of the chart) ----------
1375
- function buildTable() {
1376
- const m = metric();
1377
- const rows = [...POINTS].filter(p => p[m.key] != null).sort((a, b) =>
1378
- m.lowerBetter ? a[m.key] - b[m.key] : b[m.key] - a[m.key]);
1379
- let html = "<table><thead><tr><th>Method</th><th>Variant</th><th>Family</th>";
1380
- for (const x of METRICS) html += `<th>${x.label}</th>`;
1381
- html += "<th>Imputed</th></tr></thead><tbody>";
1382
- for (const p of rows) {
1383
- html += `<tr><td>${p.method}</td><td>${p.variant || "—"}</td><td>${p.family}</td>`;
1384
- for (const x of METRICS) html += `<td>${fmtMetric(x, p[x.key])}</td>`;
1385
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1386
- }
1387
- document.getElementById("tblwrap").innerHTML = html + "</tbody></table>";
1388
- }
1389
-
1390
- // ---------- paper view ----------
1391
- setUpPaperView(render);
1392
- // The y-axis lives in its own pane, so it is offset back into place.
1393
- setUpExport(() => [{ svg: axisSvg, dx: 0 }, { svg: svg, dx: AXIS_W }], () => slugify(document.title));
1394
-
1395
- // ---------- boot ----------
1396
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1397
- let resizeTimer = null;
1398
- window.addEventListener("resize", () => {
1399
- clearTimeout(resizeTimer);
1400
- resizeTimer = setTimeout(render, 120);
1401
- });
1402
-
1403
- buildChips();
1404
- buildTable();
1405
- syncChips();
1406
- syncVariantBtns();
1407
- render(); // also builds the legend (it depends on the metric + selection)
1408
- })();
1409
- </script>
1410
- </body>
1411
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/leaderboard_table.html DELETED
@@ -1,1162 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard table — no imputation | All Tasks | Medium</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* --- The table ------------------------------------------------------------
269
- A scroll box with a sticky header, so the column names stay put through 80
270
- rows. Numbers are tabular so digits line up down a column. */
271
- .lbt-scroll {
272
- overflow: auto; max-height: 720px; margin-top: 4px;
273
- border: 1px solid var(--line); border-radius: 10px;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lbt-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
277
- .lbt-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- table.lbt { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
282
- table.lbt th, table.lbt td { padding: 5px 9px; text-align: center; border: 1px solid var(--line); }
283
- table.lbt thead th {
284
- position: sticky; top: 0; z-index: 3; background: var(--card); font-size: 12px;
285
- font-weight: 650; white-space: nowrap; cursor: pointer; user-select: none;
286
- box-shadow: inset 0 -1px 0 var(--line);
287
- }
288
- /* The header is both the sort control and the definition, so it gets the
289
- pointer of the former and the dotted underline of the latter. */
290
- table.lbt thead th .lbl { text-decoration: underline; text-decoration-style: dotted;
291
- text-decoration-color: var(--muted); text-underline-offset: 3px; }
292
- table.lbt thead th:hover { color: var(--accent); }
293
- table.lbt thead th::after { content: "\2195"; font-size: 0.72em; opacity: 0.3; margin-left: 5px; }
294
- table.lbt thead th[aria-sort="ascending"]::after { content: "\25B2"; opacity: 0.85; }
295
- table.lbt thead th[aria-sort="descending"]::after { content: "\25BC"; opacity: 0.85; }
296
- table.lbt td.name { text-align: left; white-space: nowrap; }
297
- table.lbt td.pos { color: var(--muted); font-variant-numeric: tabular-nums; }
298
- table.lbt td.type { white-space: nowrap; width: 1%; }
299
- table.lbt td.num { font-weight: 600; }
300
- table.lbt td.na { color: var(--pt-muted); }
301
- table.lbt tbody tr:hover td { filter: brightness(1.12); }
302
- .ci { opacity: 0.5; font-weight: 400; font-size: 0.82em; }
303
- .pill { padding: 1px 7px; border-radius: 999px; font-size: 0.95em; white-space: nowrap; }
304
- .verified { font-size: 0.85em; }
305
- .link-icon { font-size: 0.78em; opacity: 0.65; margin-left: 2px; }
306
- /* Grey rather than a dimmed inherit: dimming leaves the tag tinted with the
307
- family colour, which the model name already carries, and made this table
308
- disagree with the cross-subset table on the app page. */
309
- .variant-tag { color: var(--muted); font-weight: 400; font-size: 0.9em; }
310
- td.name .imp-mark { color: var(--muted); font-weight: 700; margin-left: 3px; }
311
- td.name a { text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
312
- td.name a:hover { text-decoration-style: solid; }
313
-
314
- .lbt-search {
315
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
316
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
317
- padding: 6px 9px; width: 12em;
318
- }
319
- .lbt-cap { font-size: 12.5px; color: var(--muted); margin: 7px 0 0; }
320
- .lbt-empty { padding: 34px 0; text-align: center; color: var(--muted); font-size: 13px; }
321
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
322
- /* The model selector sits above the table it filters. Families pack side by
323
- side rather than stacking: one row each would leave the small ones (Baseline,
324
- Other) wasting a line. */
325
- .chips { margin: 2px 0 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
326
- .chiprow { flex: 0 1 auto; min-width: 0; }
327
-
328
- /* The data export, in the same green the website's figure-export controls use,
329
- so "this downloads something" reads the same across the site. */
330
- .btn.export {
331
- color: #063; background: #7ee0b8; border-color: #7ee0b8; font-weight: 700;
332
- }
333
- .btn.export:hover { background: #a6efcd; border-color: #a6efcd; }
334
- /* Paper view keeps the table: here the table *is* the figure. */
335
- body.paper .lbt-scroll { max-height: none; }
336
- </style>
337
- </head>
338
- <body>
339
- <div class="viewbar">
340
- <button class="btn" id="btn-paper" title="White background, table only — for slides and papers">Paper view</button>
341
- </div>
342
- <p class="explorer-title" id="title"></p>
343
- <div class="controls">
344
- <span class="grouplabel">Variants</span>
345
- <div class="btnrow" id="variant-btns"></div>
346
- <button class="btn" id="btn-imputed" title="Models whose score is partly imputed, marked &#8225;"></button>
347
- <input class="lbt-search" id="search" type="search" placeholder="Search model or family" aria-label="Search">
348
- <span class="grouplabel">Columns</span>
349
- <div class="btnrow" id="col-btns"></div>
350
- <button class="btn export" id="btn-csv" title="Download the rows and columns shown, in the current sort order">Download CSV</button>
351
- </div>
352
- <!-- The shared paper-view helper reveals this bar; a table has no figure to
353
- export as SVG/PNG/PDF, and its CSV button belongs beside the filters that
354
- shape the export, so it stays empty here. -->
355
- <div class="exportbar" id="exportbar" hidden></div>
356
- <!-- The model selector belongs above the table it filters. It doubles as the
357
- family key, which is why there is no separate legend strip. -->
358
- <div class="chips" id="chips"></div>
359
- <div class="lbt-scroll" id="tblwrap"></div>
360
- <p class="lbt-cap" id="caption"></p>
361
-
362
- <script>
363
- (function () {
364
- "use strict";
365
- const CONFIG = {"title": null, "columns": [{"key": "position", "label": "#", "always": true, "hint": "Position in this subset's published ranking."}, {"key": "family", "label": "Type", "always": true, "hint": "Model family \u2014 see the key above the table."}, {"key": "model", "label": "Model", "always": true, "hint": "The model, its tuning variant in brackets, \u2714\ufe0f when the implementation was verified, and \u2021 when some results are imputed."}, {"key": "elo", "label": "Elo", "decimals": 0, "heatmap": true, "hint": "Pairwise win-rate rating; a 400-point gap is about a 91% win rate. Higher is better."}, {"key": "score", "label": "Score", "decimals": 3, "heatmap": true, "hint": "Error rescaled per dataset to 1 (best) \u2026 0 (median), then averaged. Higher is better."}, {"key": "rank", "label": "Rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Mean rank across datasets. Lower is better."}, {"key": "hrank", "label": "Harmonic rank", "short": "H. rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Harmonic mean of per-dataset ranks; rewards being excellent somewhere. Lower is better."}, {"key": "imp", "label": "Improvability (%)", "short": "Improv.", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "How much lower the best model's error is than this one's, per dataset. Lower is better."}, {"key": "train_time", "label": "Train time (s/1K)", "short": "Train s", "decimals": 2, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to fit per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "predict_time", "label": "Predict time (s/1K)", "short": "Predict s", "decimals": 3, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to predict per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "imputed_pct", "label": "Imputed (%)", "short": "Imputed", "decimals": 1, "lowerBetter": true, "heatmap": true, "hint": "Share of datasets whose score was imputed because the model could not run on them."}, {"key": "hardware", "label": "Hardware", "text": true, "hint": "The hardware the reported runtimes were measured on."}], "rankKey": "elo", "variants": ["Default", "Tuned", "Tuned + Ens."]};
366
- const POINTS = [{"position":0,"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1986.0,"score":0.867,"rank":4.41,"hrank":1.69,"imp":2.683,"train_time":27.29,"predict_time":13.119,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+306\/-145","imputed":false},{"position":1,"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1899.0,"score":0.71,"rank":6.33,"hrank":2.98,"imp":4.858,"train_time":191.18,"predict_time":2.207,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+85\/-49","imputed":false},{"position":2,"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1794.0,"score":0.659,"rank":9.55,"hrank":3.26,"imp":5.878,"train_time":1.01,"predict_time":0.215,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+214\/-135","imputed":false},{"position":3,"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1775.0,"score":0.579,"rank":10.23,"hrank":4.46,"imp":6.048,"train_time":432.36,"predict_time":4.085,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+83\/-51","imputed":false},{"position":4,"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1722.0,"score":0.504,"rank":12.39,"hrank":8.78,"imp":7.846,"train_time":2.76,"predict_time":0.701,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+130\/-75","imputed":false},{"position":5,"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1697.0,"score":0.541,"rank":13.53,"hrank":4.41,"imp":6.851,"train_time":0.6,"predict_time":0.119,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+186\/-110","imputed":false},{"position":6,"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1646.0,"score":0.446,"rank":16.07,"hrank":8.48,"imp":8.576,"train_time":735.58,"predict_time":11.736,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+142\/-104","imputed":false},{"position":7,"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1625.0,"score":0.412,"rank":17.17,"hrank":11.86,"imp":8.917,"train_time":1719.82,"predict_time":1.675,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+92\/-92","imputed":false},{"position":8,"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1615.0,"score":0.397,"rank":17.73,"hrank":12.25,"imp":9.07,"train_time":2526.31,"predict_time":6.013,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+104\/-105","imputed":false},{"position":9,"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1611.0,"score":0.379,"rank":17.97,"hrank":9.32,"imp":8.932,"train_time":489.93,"predict_time":0.249,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+85\/-63","imputed":false},{"position":10,"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1600.0,"score":0.403,"rank":18.55,"hrank":10.47,"imp":9.218,"train_time":735.58,"predict_time":1.386,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+126\/-99","imputed":false},{"position":11,"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1596.0,"score":0.372,"rank":18.8,"hrank":9.43,"imp":9.14,"train_time":489.93,"predict_time":0.02,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+98\/-66","imputed":false},{"position":12,"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1589.0,"score":0.327,"rank":19.19,"hrank":13.5,"imp":10.466,"train_time":131.56,"predict_time":2.639,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+68\/-51","imputed":false},{"position":13,"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1576.0,"score":0.378,"rank":19.95,"hrank":9.61,"imp":9.095,"train_time":2.47,"predict_time":0.01,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+103\/-101","imputed":false},{"position":14,"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1558.0,"score":0.322,"rank":20.99,"hrank":15.67,"imp":10.221,"train_time":2067.95,"predict_time":65.195,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+70\/-52","imputed":false},{"position":15,"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1548.0,"score":0.295,"rank":21.61,"hrank":14.86,"imp":10.546,"train_time":282.13,"predict_time":0.563,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+81\/-55","imputed":false},{"position":16,"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1544.0,"score":0.292,"rank":21.87,"hrank":13.14,"imp":10.254,"train_time":1236.88,"predict_time":0.579,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+146\/-87","imputed":false},{"position":17,"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1533.0,"score":0.301,"rank":22.52,"hrank":14.08,"imp":10.104,"train_time":1719.82,"predict_time":0.081,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+105\/-89","imputed":false},{"position":18,"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1530.0,"score":0.324,"rank":22.69,"hrank":14.72,"imp":10.077,"train_time":1.88,"predict_time":0.645,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+136\/-79","imputed":false},{"position":19,"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1526.0,"score":0.355,"rank":22.94,"hrank":10.98,"imp":10.809,"train_time":0.75,"predict_time":0.18,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+156\/-139","imputed":false},{"position":20,"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1522.0,"score":0.319,"rank":23.22,"hrank":6.34,"imp":10.287,"train_time":4786.6,"predict_time":444.544,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+134\/-119","imputed":false},{"position":21,"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1517.0,"score":0.314,"rank":23.53,"hrank":13.31,"imp":11.684,"train_time":95.92,"predict_time":0.297,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+116\/-121","imputed":false},{"position":22,"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1506.0,"score":0.216,"rank":24.2,"hrank":18.44,"imp":11.603,"train_time":131.56,"predict_time":0.132,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+76\/-54","imputed":false},{"position":23,"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1501.0,"score":0.234,"rank":24.53,"hrank":20.07,"imp":11.102,"train_time":282.13,"predict_time":0.066,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+64\/-55","imputed":false},{"position":24,"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1481.0,"score":0.234,"rank":25.86,"hrank":15.63,"imp":11.089,"train_time":1236.88,"predict_time":0.054,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+130\/-76","imputed":false},{"position":25,"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1472.0,"score":0.217,"rank":26.42,"hrank":20.56,"imp":11.215,"train_time":2526.31,"predict_time":0.349,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+92\/-81","imputed":false},{"position":26,"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1470.0,"score":0.266,"rank":26.58,"hrank":15.47,"imp":12.146,"train_time":95.92,"predict_time":0.027,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+106\/-115","imputed":false},{"position":27,"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1423.0,"score":0.224,"rank":29.66,"hrank":21.18,"imp":12.482,"train_time":566.74,"predict_time":0.951,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+106\/-114","imputed":false},{"position":28,"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1423.0,"score":0.155,"rank":29.69,"hrank":18.8,"imp":12.112,"train_time":2067.95,"predict_time":10.881,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+97\/-63","imputed":false},{"position":29,"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1418.0,"score":0.203,"rank":30.0,"hrank":19.33,"imp":13.176,"train_time":0.48,"predict_time":0.023,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+112\/-119","imputed":false},{"position":30,"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1404.0,"score":0.221,"rank":30.97,"hrank":12.53,"imp":12.662,"train_time":4786.6,"predict_time":42.641,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+123\/-118","imputed":false},{"position":31,"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1377.0,"score":0.128,"rank":32.79,"hrank":26.34,"imp":12.45,"train_time":1770.56,"predict_time":1.069,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+71\/-61","imputed":false},{"position":32,"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1373.0,"score":0.159,"rank":33.01,"hrank":27.27,"imp":13.982,"train_time":566.74,"predict_time":0.06,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+92\/-101","imputed":false},{"position":33,"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1372.0,"score":0.137,"rank":33.13,"hrank":21.73,"imp":12.907,"train_time":0.49,"predict_time":0.053,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+89\/-103","imputed":false},{"position":34,"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1344.0,"score":0.157,"rank":34.99,"hrank":24.16,"imp":13.337,"train_time":0.51,"predict_time":0.127,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+118\/-102","imputed":false},{"position":35,"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1332.0,"score":0.174,"rank":35.84,"hrank":16.46,"imp":15.311,"train_time":46.62,"predict_time":43.738,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+119\/-134","imputed":false},{"position":36,"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1326.0,"score":0.096,"rank":36.23,"hrank":29.85,"imp":13.982,"train_time":2.13,"predict_time":0.047,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+110\/-88","imputed":false},{"position":37,"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1326.0,"score":0.134,"rank":36.24,"hrank":27.26,"imp":15.595,"train_time":695.61,"predict_time":0.085,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+113\/-128","imputed":false},{"position":38,"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1298.0,"score":0.073,"rank":38.1,"hrank":33.85,"imp":14.33,"train_time":8.06,"predict_time":0.25,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+72\/-79","imputed":false},{"position":39,"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1279.0,"score":0.17,"rank":39.33,"hrank":9.92,"imp":16.418,"train_time":6.55,"predict_time":5.409,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+140\/-160","imputed":false},{"position":40,"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1272.0,"score":0.049,"rank":39.78,"hrank":35.01,"imp":14.114,"train_time":1770.56,"predict_time":0.051,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+70\/-75","imputed":false},{"position":41,"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1272.0,"score":0.034,"rank":39.84,"hrank":36.58,"imp":15.091,"train_time":6.75,"predict_time":0.235,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+73\/-68","imputed":false},{"position":42,"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1265.0,"score":0.089,"rank":40.24,"hrank":32.83,"imp":16.441,"train_time":695.61,"predict_time":0.006,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+104\/-133","imputed":false},{"position":43,"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1258.0,"score":0.028,"rank":40.73,"hrank":38.32,"imp":14.82,"train_time":0.29,"predict_time":0.045,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-64","imputed":false},{"position":44,"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1244.0,"score":0.03,"rank":41.64,"hrank":35.97,"imp":15.1,"train_time":66.0,"predict_time":0.341,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+85\/-85","imputed":false},{"position":45,"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1221.0,"score":0.057,"rank":43.14,"hrank":37.72,"imp":17.87,"train_time":2.18,"predict_time":0.005,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+88\/-136","imputed":false},{"position":46,"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1217.0,"score":0.022,"rank":43.4,"hrank":40.85,"imp":15.304,"train_time":49.61,"predict_time":0.199,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+79\/-89","imputed":false},{"position":47,"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1214.0,"score":0.031,"rank":43.54,"hrank":37.85,"imp":15.744,"train_time":66.0,"predict_time":0.024,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+86\/-84","imputed":false},{"position":48,"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1201.0,"score":0.06,"rank":44.39,"hrank":36.49,"imp":17.192,"train_time":351.94,"predict_time":1.658,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+110\/-138","imputed":false},{"position":49,"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1201.0,"score":0.028,"rank":44.41,"hrank":39.08,"imp":16.933,"train_time":11.44,"predict_time":10.416,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+82\/-95","imputed":false},{"position":50,"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1179.0,"score":0.033,"rank":45.73,"hrank":41.33,"imp":18.488,"train_time":88.27,"predict_time":9.62,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+84\/-111","imputed":false},{"position":51,"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1174.0,"score":0.008,"rank":46.07,"hrank":43.76,"imp":16.303,"train_time":49.61,"predict_time":0.015,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+70\/-83","imputed":false},{"position":52,"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1126.0,"score":0.001,"rank":48.9,"hrank":47.16,"imp":18.145,"train_time":2.18,"predict_time":0.039,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+59\/-72","imputed":false},{"position":53,"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1110.0,"score":0.019,"rank":49.83,"hrank":43.02,"imp":21.633,"train_time":20.26,"predict_time":0.515,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+101\/-125","imputed":false},{"position":54,"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1101.0,"score":0.015,"rank":50.32,"hrank":45.23,"imp":19.294,"train_time":351.94,"predict_time":0.114,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+91\/-105","imputed":false},{"position":55,"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1060.0,"score":0.004,"rank":52.57,"hrank":48.1,"imp":22.747,"train_time":20.26,"predict_time":0.2,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+92\/-116","imputed":false},{"position":56,"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1046.0,"score":0.018,"rank":53.27,"hrank":47.13,"imp":25.98,"train_time":2.49,"predict_time":0.145,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+147\/-184","imputed":false},{"position":57,"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1031.0,"score":0.018,"rank":54.04,"hrank":48.76,"imp":22.864,"train_time":1.51,"predict_time":0.111,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+136\/-171","imputed":false},{"position":58,"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1000.0,"score":0.0,"rank":55.55,"hrank":54.46,"imp":21.197,"train_time":0.2,"predict_time":0.021,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-92","imputed":false},{"position":59,"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":989.0,"score":0.005,"rank":56.06,"hrank":49.75,"imp":24.353,"train_time":150.3,"predict_time":10.071,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+94\/-128","imputed":false},{"position":60,"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":988.0,"score":0.0,"rank":56.13,"hrank":54.65,"imp":25.807,"train_time":3381.77,"predict_time":1.724,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+56\/-90","imputed":false},{"position":61,"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":987.0,"score":0.0,"rank":56.18,"hrank":54.79,"imp":25.781,"train_time":3381.77,"predict_time":5.283,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+61\/-86","imputed":false},{"position":62,"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":976.0,"score":0.0,"rank":56.64,"hrank":54.71,"imp":22.789,"train_time":0.48,"predict_time":0.073,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+94\/-121","imputed":false},{"position":63,"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":935.0,"score":0.004,"rank":58.45,"hrank":53.87,"imp":32.872,"train_time":43.64,"predict_time":0.081,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+153\/-226","imputed":false},{"position":64,"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":901.0,"score":0.0,"rank":59.81,"hrank":57.82,"imp":29.424,"train_time":1.5,"predict_time":0.015,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+106\/-153","imputed":false},{"position":65,"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":892.0,"score":0.002,"rank":60.16,"hrank":56.48,"imp":33.73,"train_time":43.64,"predict_time":0.018,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+154\/-245","imputed":false},{"position":66,"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":848.0,"score":0.0,"rank":61.7,"hrank":61.11,"imp":30.685,"train_time":110.78,"predict_time":1.964,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+50\/-88","imputed":false},{"position":67,"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":814.0,"score":0.0,"rank":62.78,"hrank":62.5,"imp":29.973,"train_time":150.3,"predict_time":0.482,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+53\/-106","imputed":false},{"position":68,"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":797.0,"score":0.0,"rank":63.27,"hrank":61.49,"imp":35.794,"train_time":0.19,"predict_time":0.019,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+137\/-244","imputed":false},{"position":69,"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":593.0,"score":0.0,"rank":67.65,"hrank":67.52,"imp":43.501,"train_time":0.1,"predict_time":0.072,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+83\/-146","imputed":false}];
367
-
368
-
369
- const NS = "http://www.w3.org/2000/svg";
370
- // Baseline and Other are one bucket, as in the site's own type legend: they
371
- // already share a color, and each holds only a handful of methods.
372
- const FAM_MERGED = "Baseline / Other";
373
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
374
- // Normalized up front so every later lookup — colors, chips, sorting — sees
375
- // the merged family. Both templates declare POINTS above this block.
376
- for (const p of POINTS) p.family = famOf(p.family);
377
-
378
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
379
- const FAM_VAR = {
380
- "Foundation Model": "var(--fam-foundation)",
381
- "Tree-based": "var(--fam-tree)",
382
- "Neural Network": "var(--fam-nn)",
383
- "Reference Pipeline": "var(--fam-reference)",
384
- [FAM_MERGED]: "var(--fam-baseline)",
385
- };
386
- // The symbol the website shows for each family, so a family chip here reads the
387
- // same as the Type column on the site. Baseline and Other are one bucket, so
388
- // that chip carries both symbols.
389
- const FAM_SYMBOL = {
390
- "Foundation Model": "🧠⚡",
391
- "Tree-based": "🌳",
392
- "Neural Network": "🧠🔁",
393
- "Reference Pipeline": "📊",
394
- [FAM_MERGED]: "📏 ❓",
395
- };
396
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
397
- const VARIANT_VAR = {
398
- "Default": "var(--var-default)",
399
- "Tuned": "var(--var-tuned)",
400
- "Tuned + Ens.": "var(--var-tunedens)",
401
- };
402
-
403
- // A family chip's label: its symbol, its name and how many methods it holds.
404
- function famChipLabel(family, count) {
405
- const symbol = FAM_SYMBOL[family];
406
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
407
- family + ' <span class="count">&times;' + count + "</span>";
408
- }
409
-
410
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
411
- const FAM_INK = {
412
- "Foundation Model": "var(--fam-foundation-ink)",
413
- "Tree-based": "var(--fam-tree-ink)",
414
- "Neural Network": "var(--fam-nn-ink)",
415
- "Reference Pipeline": "var(--fam-reference-ink)",
416
- [FAM_MERGED]: "var(--fam-baseline-ink)",
417
- };
418
-
419
- // Create an SVG element with attributes, optionally appended to `parent`.
420
- function el(name, attrs, parent) {
421
- const node = document.createElementNS(NS, name);
422
- for (const k in attrs) node.setAttribute(k, attrs[k]);
423
- if (parent) parent.appendChild(node);
424
- return node;
425
- }
426
-
427
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
428
- // locale-independent by definition, which is the point: `toLocaleString`
429
- // would follow the *viewer's* browser locale and print 1234,5 for a German
430
- // visitor, disagreeing with the figures and CSVs beside it.
431
- function fmtNum(v, decimals) {
432
- if (v == null || !isFinite(v)) return "—";
433
- return v.toFixed(decimals);
434
- }
435
-
436
- function fmtMetric(metric, v) {
437
- if (v == null || !isFinite(v)) return "—";
438
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
439
- }
440
-
441
- function fmtTime(v) {
442
- if (v >= 100) return fmtNum(v, 0) + " s";
443
- if (v >= 1) return fmtNum(v, 1) + " s";
444
- if (v >= 0.1) return fmtNum(v, 2) + " s";
445
- return fmtNum(v, 3) + " s";
446
- }
447
-
448
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
449
- function niceStep(raw) {
450
- if (!(raw > 0)) return 1;
451
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
452
- for (const m of [1, 2, 2.5, 5, 10]) {
453
- if (mag * m >= raw) return mag * m;
454
- }
455
- return mag * 10;
456
- }
457
-
458
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
459
- // index rather than by accumulation so fractional steps do not drift.
460
- function ticks(min, max, target) {
461
- const step = niceStep((max - min) / target);
462
- const first = Math.ceil(min / step);
463
- const out = [];
464
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
465
- return out.length ? out : [min];
466
- }
467
-
468
- // A hover tooltip anchored inside `box` (which must be position:relative).
469
- function makeTooltip(box) {
470
- const node = box.querySelector(".tooltip");
471
- return {
472
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
473
- move(ev) {
474
- const r = box.getBoundingClientRect();
475
- let tx = ev.clientX - r.left + 14;
476
- const ty = ev.clientY - r.top + 12;
477
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
478
- node.style.left = tx + "px";
479
- node.style.top = ty + "px";
480
- },
481
- hide() { node.style.display = "none"; },
482
- };
483
- }
484
-
485
- // Paper view — white surface, legend + chart only — is the *default*: what a
486
- // reader wants first is the figure, and it is the state worth exporting. The
487
- // controls, chip list and data table are one click away behind "Edit view".
488
- // `afterToggle` re-renders charts whose size is measured from the layout.
489
- // `options.openInPaper` (default true) decides the state the page opens in. A
490
- // chart opens as the figure; the leaderboard table opens with its controls,
491
- // since there the interaction is the point rather than scaffolding around it.
492
- function setUpPaperView(afterToggle, options) {
493
- const opts = options || {};
494
- const root = document.documentElement;
495
- let hostTheme = null; // the embedding page's choice, captured on entry
496
- const btn = document.getElementById("btn-paper");
497
- const embedded = window.parent !== window;
498
-
499
- function setPaper(on) {
500
- document.body.classList.toggle("paper", on);
501
- if (on) {
502
- hostTheme = root.getAttribute("data-theme");
503
- root.setAttribute("data-theme", "light");
504
- } else if (hostTheme) {
505
- root.setAttribute("data-theme", hostTheme);
506
- } else {
507
- root.removeAttribute("data-theme");
508
- }
509
- btn.textContent = on ? "Edit view" : "Paper view";
510
- document.getElementById("exportbar").hidden = !on || embedded;
511
- if (afterToggle) requestAnimationFrame(afterToggle);
512
- postHeight();
513
- }
514
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
515
- // Embedded, the host page owns these controls — they sit beside the panel's
516
- // static-figure toggle and are driven from the outside. Standalone (the
517
- // shareable single file) this page needs its own.
518
- if (embedded) document.querySelector(".viewbar").hidden = true;
519
- window.addEventListener("message", ev => {
520
- const d = ev.data;
521
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
522
- });
523
- // Only standalone: embedded, the host owns the button and would not see the
524
- // key press, so its label would fall out of step with the frame.
525
- if (!embedded) {
526
- document.addEventListener("keydown", ev => {
527
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
528
- });
529
- }
530
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
531
- }
532
-
533
- // --- Figure export ---------------------------------------------------------
534
- // The chart is live SVG, so a file can be built from it directly. Three things
535
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
536
- // nothing outside this document), it has no background or font of its own, and
537
- // the legend is HTML rather than part of the SVG.
538
-
539
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
540
- // cloned and its label re-emitted at the measured position. foreignObject would
541
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
542
- // break the PNG path.
543
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
544
- // once the node leaves this document.
545
- function resolveVars(root, resolve) {
546
- for (const node of [root, ...root.querySelectorAll("*")]) {
547
- for (const attr of ["fill", "stroke"]) {
548
- const value = node.getAttribute(attr);
549
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
550
- }
551
- }
552
- }
553
-
554
- function legendToSvg(container, resolve) {
555
- const base = container.getBoundingClientRect();
556
- const group = document.createElementNS(NS, "g");
557
- let height = 0;
558
- for (const item of container.querySelectorAll(".item")) {
559
- const box = item.getBoundingClientRect();
560
- if (!box.width) continue;
561
- height = Math.max(height, box.bottom - base.top);
562
- let textLeft = box.left - base.left;
563
- const glyph = item.querySelector("svg");
564
- if (glyph) {
565
- const gbox = glyph.getBoundingClientRect();
566
- const wrap = el("g", {
567
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
568
- }, group);
569
- const glyphClone = glyph.cloneNode(true);
570
- resolveVars(glyphClone, resolve);
571
- wrap.appendChild(glyphClone);
572
- textLeft = gbox.right - base.left + 5;
573
- }
574
- const label = item.textContent.trim();
575
- if (!label) continue;
576
- const colored = item.querySelector("[style*='color']");
577
- const text = el("text", {
578
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
579
- fill: resolve(getComputedStyle(colored || item).color),
580
- }, group);
581
- text.textContent = label;
582
- }
583
- return { group, height: Math.ceil(height) };
584
- }
585
-
586
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
587
- // y-axis beside the scrolling plot) still exports as one figure.
588
- function buildExportSvg(parts, legendEl, pad = 10) {
589
- const rootStyle = getComputedStyle(document.documentElement);
590
- const resolve = value => String(value).replace(
591
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
592
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
593
-
594
- let chartW = 0, chartH = 0;
595
- for (const part of parts) {
596
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
597
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
598
- }
599
-
600
- const out = document.createElementNS(NS, "svg");
601
- out.setAttribute("xmlns", NS);
602
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
603
- let top = pad;
604
- const later = []; // built after the width is known
605
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
606
- if (legend && legend.height) {
607
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
608
- later.push(() => out.appendChild(legend.group));
609
- top += legend.height + 8;
610
- }
611
-
612
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
613
- const height = top + chartH + pad;
614
- out.setAttribute("width", Math.ceil(width));
615
- out.setAttribute("height", Math.ceil(height));
616
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
617
- for (const build of later) build();
618
-
619
- for (const part of parts) {
620
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
621
- const clone = part.svg.cloneNode(true);
622
- resolveVars(clone, resolve);
623
- while (clone.firstChild) group.appendChild(clone.firstChild);
624
- }
625
- return out;
626
- }
627
-
628
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
629
- function slugify(text) {
630
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
631
- }
632
-
633
- function downloadUrl(url, filename) {
634
- const a = document.createElement("a");
635
- a.href = url;
636
- a.download = filename;
637
- document.body.appendChild(a);
638
- a.click();
639
- a.remove();
640
- }
641
-
642
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
643
- function rasterize(svg, scale, done, fail) {
644
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
645
- const img = new Image();
646
- img.onload = () => {
647
- const canvas = document.createElement("canvas");
648
- canvas.width = Math.round(width * scale);
649
- canvas.height = Math.round(height * scale);
650
- const ctx = canvas.getContext("2d");
651
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
652
- ctx.drawImage(img, 0, 0);
653
- done(canvas, width, height);
654
- };
655
- img.onerror = fail;
656
- img.src = "data:image/svg+xml;charset=utf-8,"
657
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
658
- }
659
-
660
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
661
- // cost this page its zero-dependency, single-file property. The image is stored
662
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
663
- // in points to the figure's CSS size, so it prints at the size it appears here
664
- // and the pixels land at 96*scale dpi.
665
- async function buildPdf(canvas, cssWidth, cssHeight) {
666
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
667
- const rgb = new Uint8Array((pixels.length / 4) * 3);
668
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
669
- rgb[j] = pixels[i];
670
- rgb[j + 1] = pixels[i + 1];
671
- rgb[j + 2] = pixels[i + 2];
672
- }
673
- const deflated = new Uint8Array(await new Response(
674
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
675
-
676
- const encoder = new TextEncoder();
677
- const chunks = [];
678
- const offsets = [];
679
- let cursor = 0;
680
- const put = data => {
681
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
682
- chunks.push(bytes);
683
- cursor += bytes.length;
684
- };
685
- const object = (id, body, stream) => {
686
- offsets[id] = cursor;
687
- put(`${id} 0 obj\n${body}\n`);
688
- if (stream) {
689
- put("stream\n");
690
- put(stream);
691
- put("\nendstream\n");
692
- }
693
- put("endobj\n");
694
- };
695
-
696
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
697
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
698
- put("%PDF-1.4\n");
699
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
700
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
701
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
702
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
703
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
704
- object(4, "<< /Type /XObject /Subtype /Image "
705
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
706
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
707
- object(5, `<< /Length ${content.length} >>`, content);
708
-
709
- const xref = cursor;
710
- let table = "xref\n0 6\n0000000000 65535 f \n";
711
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
712
- put(table);
713
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
714
- return new Blob(chunks, { type: "application/pdf" });
715
- }
716
-
717
- // Wire up the export buttons; `getParts` is called per click so the file always
718
- // matches what is on screen. Returns a `run(format)` the host page can drive.
719
- function setUpExport(getParts, basename) {
720
- const buttons = {
721
- svg: document.getElementById("btn-svg"),
722
- png: document.getElementById("btn-png"),
723
- pdf: document.getElementById("btn-pdf"),
724
- };
725
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
726
-
727
- // A sandboxed frame has no modals, so a failure is reported on the button.
728
- function complain(format) {
729
- const button = buttons[format];
730
- if (!button) return;
731
- const label = button.textContent;
732
- button.textContent = "failed";
733
- setTimeout(() => { button.textContent = label; }, 2500);
734
- }
735
-
736
- function run(format) {
737
- const svg = figure();
738
- const name = basename();
739
- if (format === "svg") {
740
- downloadUrl("data:image/svg+xml;charset=utf-8,"
741
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
742
- return;
743
- }
744
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
745
- // points so the pixels already land near 200 dpi at print size.
746
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
747
- if (format === "png") {
748
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
749
- } else {
750
- buildPdf(canvas, cssWidth, cssHeight)
751
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
752
- .catch(() => complain("pdf"));
753
- }
754
- }, () => complain(format));
755
- }
756
-
757
- for (const format of Object.keys(buttons)) {
758
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
759
- }
760
- // Embedded, the buttons live in the host's panel header (see main.taExport).
761
- window.addEventListener("message", ev => {
762
- const d = ev.data;
763
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
764
- });
765
- }
766
-
767
- // When embedded, report the content height so the host page can size the
768
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
769
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
770
- // clamped to at least the iframe's current viewport, which turns the
771
- // resize round-trip into a grow-forever feedback loop. The change guard
772
- // stops re-posting once the height settles.
773
- let lastPostedHeight = 0;
774
- function postHeight() {
775
- if (window.parent === window) return;
776
- const height = Math.ceil(document.body.offsetHeight);
777
- if (Math.abs(height - lastPostedHeight) < 3) return;
778
- lastPostedHeight = height;
779
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
780
- }
781
-
782
-
783
- const COLUMNS = CONFIG.columns;
784
- const RANK_KEY = CONFIG.rankKey;
785
- const colByKey = new Map(COLUMNS.map(c => [c.key, c]));
786
- const VARIANTS = CONFIG.variants.filter(v => POINTS.some(p => p.variant === v));
787
-
788
- // How a variant is written out. The short "Tuned + Ens." form is the internal key
789
- // (the charts' colour and marker tables are keyed by it), but nothing on the page
790
- // should show the abbreviation: the row tag uses the spelling the data and the
791
- // app's cross-subset table use, and the toggle spells it out in full.
792
- const VARIANT_TEXT = {
793
- "Default": "default",
794
- "Tuned": "tuned",
795
- "Tuned + Ens.": "tuned + ensembled",
796
- };
797
- const VARIANT_BTN = { "Tuned + Ens.": "Tuned + Ensembled" };
798
- const variantText = v => VARIANT_TEXT[v] || v;
799
- const HAS_IMPUTED = POINTS.some(p => p.imputed);
800
-
801
- const state = {
802
- methods: new Set(POINTS.map(p => p.method)),
803
- variants: new Set(VARIANTS),
804
- columns: new Set(COLUMNS.filter(c => c.on !== false).map(c => c.key)),
805
- imputed: true,
806
- search: "",
807
- sortKey: null,
808
- sortAsc: false,
809
- };
810
-
811
- const tblwrap = document.getElementById("tblwrap");
812
- const chipsBox = document.getElementById("chips");
813
- const famChips = new Map();
814
- const chipByMethod = new Map();
815
-
816
- // ---------- heat shading ----------
817
- // Green (best) through olive to red (worst), matching the website's
818
- // cross-subset overview so a reader moving between them reads one scale.
819
- const RAMP = [[0, [28, 120, 62]], [0.5, [138, 122, 36]], [1, [160, 58, 58]]];
820
- function heatColor(frac) {
821
- const f = Math.max(0, Math.min(1, frac));
822
- for (let i = 0; i < RAMP.length - 1; i++) {
823
- const [f0, c0] = RAMP[i], [f1, c1] = RAMP[i + 1];
824
- if (f <= f1) {
825
- const t = f1 === f0 ? 0 : (f - f0) / (f1 - f0);
826
- const mix = c0.map((v, j) => Math.round(v + t * (c1[j] - v)));
827
- return "rgb(" + mix.join(",") + ")";
828
- }
829
- }
830
- return "rgb(" + RAMP[RAMP.length - 1][1].join(",") + ")";
831
- }
832
- // Runtimes span orders of magnitude; shading them linearly paints every model
833
- // the same green and only the slowest one red, so those normalize in log space.
834
- function scaleOf(col, v) { return col.logScale ? Math.log10(v) : v; }
835
- function bounds(rows, col) {
836
- const values = [];
837
- for (const p of rows) {
838
- const v = p[col.key];
839
- if (v == null || !isFinite(v)) continue;
840
- if (col.logScale && !(v > 0)) continue;
841
- values.push(scaleOf(col, v));
842
- }
843
- if (values.length < 2) return null;
844
- const lo = Math.min(...values), hi = Math.max(...values);
845
- return hi > lo ? [lo, hi] : null;
846
- }
847
-
848
- // ---------- cells ----------
849
- function nameCell(p) {
850
- const ink = FAM_INK[p.family] || "var(--muted)";
851
- let inner = escapeHtml(p.method);
852
- if (p.variant) inner += ' <span class="variant-tag">(' + escapeHtml(variantText(p.variant)) + ")</span>";
853
- if (p.verified) inner += ' <span class="verified" title="Verified implementation">&#10004;&#65039;</span>';
854
- if (p.imputed) {
855
- const pct = isFinite(p.imputed_pct) ? fmtNum(p.imputed_pct, 0) + "% " : "";
856
- inner += ' <span class="imp-mark" title="' + pct + 'imputed">&#8225;</span>';
857
- }
858
- const body = p.url
859
- ? '<a href="' + escapeHtml(p.url) + '" target="_blank" rel="noopener" style="color:' + ink +
860
- ';font-weight:600;">' + inner + '<span class="link-icon">&#8599;</span></a>'
861
- : '<span style="color:' + ink + ';font-weight:600;">' + inner + "</span>";
862
- return '<td class="name" data-export="' + escapeHtml(plainName(p)) + '">' + body + "</td>";
863
- }
864
- function plainName(p) {
865
- return p.method + (p.variant ? " (" + p.variant + ")" : "");
866
- }
867
- function escapeHtml(text) {
868
- return String(text == null ? "" : text).replace(/[&<>"']/g, c =>
869
- ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
870
- }
871
-
872
- // ---------- rows ----------
873
- function visibleRows() {
874
- const term = state.search.trim().toLowerCase();
875
- return POINTS.filter(p => {
876
- if (!state.methods.has(p.method)) return false;
877
- if (p.variant && !state.variants.has(p.variant)) return false;
878
- if (!state.imputed && p.imputed) return false;
879
- if (term && !(p.method + " " + (p.variant || "") + " " + p.family).toLowerCase().includes(term)) return false;
880
- return true;
881
- });
882
- }
883
- function sortRows(rows) {
884
- const out = rows.slice();
885
- if (!state.sortKey) return out.sort((a, b) => a.position - b.position);
886
- const col = colByKey.get(state.sortKey);
887
- const sign = state.sortAsc ? 1 : -1;
888
- const keyOf = p => {
889
- const v = p[state.sortKey];
890
- if (col && col.text) return String(v == null ? "" : v).toLowerCase();
891
- return v == null || !isFinite(v) ? null : v;
892
- };
893
- return out.sort((a, b) => {
894
- const ka = keyOf(a), kb = keyOf(b);
895
- if (ka === null || kb === null) return ka === kb ? 0 : ka === null ? 1 : -1;
896
- return ka < kb ? -sign : ka > kb ? sign : 0;
897
- });
898
- }
899
-
900
- function activeColumns() {
901
- return COLUMNS.filter(c => c.always || state.columns.has(c.key));
902
- }
903
-
904
- function render() {
905
- const rows = sortRows(visibleRows());
906
- const cols = activeColumns();
907
- if (!rows.length) {
908
- tblwrap.innerHTML = '<p class="lbt-empty">Nothing selected — turn a family or variant back on.</p>';
909
- document.getElementById("caption").textContent = "";
910
- postHeight();
911
- return;
912
- }
913
- // No medals here: this table is the full ranking, so the position column
914
- // already says who is where. They belong to the cross-subset overview.
915
- const heat = new Map();
916
- for (const col of cols) if (col.heatmap) heat.set(col.key, bounds(rows, col));
917
-
918
- let html = '<table class="lbt"><thead><tr>';
919
- for (const col of cols) {
920
- const sorted = state.sortKey === col.key
921
- ? ' aria-sort="' + (state.sortAsc ? "ascending" : "descending") + '"' : "";
922
- html += "<th" + sorted + ' data-key="' + col.key + '" title="' + escapeHtml(col.hint || col.label) +
923
- '"><span class="lbl">' + escapeHtml(col.label) + "</span>" +
924
- (col.key === "elo" ? ' <span class="ci">(95% CI)</span>' : "") + "</th>";
925
- }
926
- html += "</tr></thead><tbody>";
927
- for (const p of rows) {
928
- html += "<tr>";
929
- for (const col of cols) {
930
- if (col.key === "position") {
931
- html += '<td class="pos num" data-sort="' + p.position + '">' + p.position + "</td>";
932
- } else if (col.key === "family") {
933
- const mark = FAM_VAR[p.family] || "var(--fam-baseline)";
934
- html += '<td class="type" data-sort="' + escapeHtml(p.family) + '" data-export="' + escapeHtml(p.family) +
935
- '"><span class="pill" style="background:color-mix(in srgb, ' + mark +
936
- ' 13%, transparent);color:' + (FAM_INK[p.family] || "var(--muted)") +
937
- ";border:1px solid color-mix(in srgb, " + mark + ' 40%, transparent);">' +
938
- escapeHtml(p.family_symbol || "") + "</span></td>";
939
- } else if (col.key === "model") {
940
- html += nameCell(p);
941
- } else {
942
- html += valueCell(p, col, heat.get(col.key));
943
- }
944
- }
945
- html += "</tr>";
946
- }
947
- tblwrap.innerHTML = html + "</tbody></table>";
948
- for (const th of tblwrap.querySelectorAll("th")) {
949
- th.addEventListener("click", () => sortBy(th.dataset.key));
950
- }
951
- document.getElementById("caption").textContent =
952
- rows.length + (rows.length === 1 ? " row" : " rows") +
953
- " · click a column header to sort, hover one for what it means" +
954
- (heat.size ? " · green is better, red is worse, per column" : "") +
955
- (HAS_IMPUTED ? " · ‡ marks a partly imputed score" : "");
956
- postHeight();
957
- }
958
-
959
- function valueCell(p, col, span) {
960
- const v = p[col.key];
961
- if (v == null || (typeof v === "number" && !isFinite(v))) return '<td class="na">&ndash;</td>';
962
- if (col.text) {
963
- return '<td data-sort="' + escapeHtml(v) + '" data-export="' + escapeHtml(v) + '">' + escapeHtml(v) + "</td>";
964
- }
965
- let style = "";
966
- if (span) {
967
- let frac = (scaleOf(col, v) - span[0]) / (span[1] - span[0]);
968
- if (!col.lowerBetter) frac = 1 - frac;
969
- style = ' style="background:' + heatColor(frac) + ';color:#f7f7f7;"';
970
- }
971
- let text = fmtNum(v, col.decimals) + (col.suffix || "");
972
- let ci = "";
973
- if (col.key === "elo" && p.elo_ci) {
974
- ci = ' <span class="ci">(' + escapeHtml(p.elo_ci) + ")</span>";
975
- }
976
- return '<td class="num" data-sort="' + v + '"' + (ci ? ' data-ci="' + escapeHtml(p.elo_ci) + '"' : "") +
977
- style + ">" + text + ci + "</td>";
978
- }
979
-
980
- function sortBy(key) {
981
- if (!key) return;
982
- if (state.sortKey === key) state.sortAsc = !state.sortAsc;
983
- else { state.sortKey = key; state.sortAsc = !!(colByKey.get(key) || {}).lowerBetter; }
984
- render();
985
- }
986
-
987
- // ---------- chips (families and their methods) ----------
988
- function familyMembers(fam) {
989
- return [...new Set(POINTS.filter(p => p.family === fam).map(p => p.method))];
990
- }
991
- function methodRank(name) {
992
- const col = colByKey.get(RANK_KEY);
993
- const values = POINTS.filter(p => p.method === name && isFinite(p[RANK_KEY])).map(p => p[RANK_KEY]);
994
- if (!values.length) return Infinity;
995
- return col.lowerBetter ? Math.min(...values) : -Math.max(...values);
996
- }
997
- function buildChips() {
998
- const head = document.createElement("div");
999
- head.className = "chips-head";
1000
- head.textContent = "Models shown — click to remove, click a family to toggle the whole group";
1001
- chipsBox.appendChild(head);
1002
- for (const fam of FAM_ORDER) {
1003
- const members = familyMembers(fam);
1004
- if (!members.length) continue;
1005
- members.sort((a, b) => methodRank(a) - methodRank(b));
1006
- const row = document.createElement("div");
1007
- row.className = "chiprow";
1008
- const famBtn = document.createElement("button");
1009
- famBtn.className = "famchip";
1010
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1011
- famBtn.innerHTML = famChipLabel(fam, members.length);
1012
- famBtn.title = "Toggle all " + members.length + " " + fam + " models";
1013
- famBtn.addEventListener("click", () => toggleFamily(fam));
1014
- row.appendChild(famBtn);
1015
- famChips.set(fam, famBtn);
1016
- const set = document.createElement("div");
1017
- set.className = "chipset";
1018
- for (const name of members) {
1019
- const imputed = POINTS.some(p => p.method === name && p.imputed);
1020
- const b = document.createElement("button");
1021
- b.className = "chip";
1022
- b.style.setProperty("--fam", FAM_VAR[fam]);
1023
- b.innerHTML = '<span class="dot"></span><span>' + escapeHtml(name) + "</span>" +
1024
- (imputed ? '<span class="imp-mark">&#8225;</span>' : "");
1025
- b.title = name + (imputed ? " — partially imputed" : "");
1026
- b.addEventListener("click", () => toggleMethod(name));
1027
- set.appendChild(b);
1028
- chipByMethod.set(name, b);
1029
- }
1030
- row.appendChild(set);
1031
- chipsBox.appendChild(row);
1032
- }
1033
- }
1034
- function syncChips() {
1035
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.methods.has(name)));
1036
- for (const [fam, b] of famChips) {
1037
- b.setAttribute("aria-pressed", String(familyMembers(fam).every(m => state.methods.has(m))));
1038
- }
1039
- }
1040
- function toggleMethod(name) {
1041
- if (state.methods.has(name)) state.methods.delete(name); else state.methods.add(name);
1042
- syncChips();
1043
- render();
1044
- }
1045
- function toggleFamily(fam) {
1046
- const members = familyMembers(fam);
1047
- const allOn = members.every(m => state.methods.has(m));
1048
- for (const m of members) { if (allOn) state.methods.delete(m); else state.methods.add(m); }
1049
- syncChips();
1050
- render();
1051
- }
1052
-
1053
- // ---------- variant / imputed / column toggles ----------
1054
- const variantBtns = new Map();
1055
- function buildVariantBtns() {
1056
- const box = document.getElementById("variant-btns");
1057
- for (const v of VARIANTS) {
1058
- const b = document.createElement("button");
1059
- b.className = "btn toggle";
1060
- b.innerHTML = '<span class="swatch"></span>' + escapeHtml(VARIANT_BTN[v] || v);
1061
- b.style.setProperty("--fam", VARIANT_VAR[v] || "var(--accent)");
1062
- b.title = "Show or hide the " + variantText(v) + " results";
1063
- b.addEventListener("click", () => {
1064
- if (state.variants.has(v)) state.variants.delete(v); else state.variants.add(v);
1065
- syncVariantBtns();
1066
- render();
1067
- });
1068
- box.appendChild(b);
1069
- variantBtns.set(v, b);
1070
- }
1071
- }
1072
- function syncVariantBtns() {
1073
- for (const [v, b] of variantBtns) b.setAttribute("aria-pressed", String(state.variants.has(v)));
1074
- const imp = document.getElementById("btn-imputed");
1075
- imp.hidden = !HAS_IMPUTED;
1076
- imp.className = "btn toggle";
1077
- imp.innerHTML = '<span class="swatch"></span>&#8225; Imputed';
1078
- imp.setAttribute("aria-pressed", String(state.imputed));
1079
- imp.style.setProperty("--fam", "var(--muted)");
1080
- }
1081
- function buildColumnBtns() {
1082
- const box = document.getElementById("col-btns");
1083
- for (const col of COLUMNS) {
1084
- if (col.always) continue;
1085
- const b = document.createElement("button");
1086
- b.className = "btn toggle";
1087
- b.textContent = col.short || col.label;
1088
- b.title = "Show or hide " + col.label;
1089
- b.style.setProperty("--fam", "var(--accent)");
1090
- b.addEventListener("click", () => {
1091
- if (state.columns.has(col.key)) state.columns.delete(col.key); else state.columns.add(col.key);
1092
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1093
- render();
1094
- });
1095
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1096
- box.appendChild(b);
1097
- }
1098
- }
1099
-
1100
- // ---------- CSV ----------
1101
- // Exports what the reader is looking at: the rows the filters left, the
1102
- // columns they picked, in the order they sorted.
1103
- function downloadCsv() {
1104
- const cols = activeColumns();
1105
- const rows = sortRows(visibleRows());
1106
- const quote = v => '"' + String(v == null ? "" : v).replace(/"/g, '""') + '"';
1107
- const head = [];
1108
- for (const col of cols) {
1109
- head.push(col.label);
1110
- if (col.key === "elo" && rows.some(p => p.elo_ci)) head.push("Elo 95% CI");
1111
- }
1112
- const lines = [head.map(quote).join(",")];
1113
- for (const p of rows) {
1114
- const values = [];
1115
- for (const col of cols) {
1116
- if (col.key === "model") values.push(plainName(p));
1117
- else values.push(p[col.key]);
1118
- if (col.key === "elo" && rows.some(q => q.elo_ci)) values.push(p.elo_ci || "");
1119
- }
1120
- lines.push(values.map(quote).join(","));
1121
- }
1122
- const blob = new Blob([lines.join("\n") + "\n"], { type: "text/csv;charset=utf-8" });
1123
- const url = URL.createObjectURL(blob);
1124
- downloadUrl(url, slugify(CONFIG.title || document.title || "leaderboard") + ".csv");
1125
- URL.revokeObjectURL(url);
1126
- }
1127
-
1128
- // ---------- boot ----------
1129
- if (CONFIG.title) document.getElementById("title").textContent = CONFIG.title;
1130
- document.getElementById("search").addEventListener("input", ev => {
1131
- state.search = ev.target.value;
1132
- render();
1133
- });
1134
- document.getElementById("btn-imputed").addEventListener("click", () => {
1135
- state.imputed = !state.imputed;
1136
- syncVariantBtns();
1137
- render();
1138
- });
1139
- document.getElementById("btn-csv").addEventListener("click", downloadCsv);
1140
- // Embedded, the host page puts its own CSV button in the panel header (next to
1141
- // the title) and asks for the download over postMessage, the same way it drives
1142
- // the figure exports. Standalone, this page needs its own button.
1143
- if (window.parent !== window) {
1144
- document.getElementById("btn-csv").hidden = true;
1145
- window.addEventListener("message", ev => {
1146
- if (ev.data && ev.data.type === "tabarena-leaderboard-csv") downloadCsv();
1147
- });
1148
- }
1149
- // Opens with the controls: the filters are the point of this page, not chrome
1150
- // around a figure.
1151
- setUpPaperView(render, { openInPaper: false });
1152
- buildVariantBtns();
1153
- buildColumnBtns();
1154
- buildChips();
1155
- syncChips();
1156
- syncVariantBtns();
1157
- render();
1158
- window.addEventListener("resize", postHeight);
1159
- })();
1160
- </script>
1161
- </body>
1162
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/n_datasets_15 DELETED
File without changes
data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_explorer.html DELETED
@@ -1,1230 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena Pareto explorer</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Two-column layout: controls + chips in a side panel, chart beside it.
269
- ``chips-right`` mirrors the columns. Wraps to stacked when narrow. */
270
- .explorer-grid { display: flex; gap: 18px; align-items: flex-start; }
271
- .explorer-grid.chips-right { flex-direction: row-reverse; }
272
- .sidebox { flex: 0 0 330px; min-width: 250px; display: flex; flex-direction: column; gap: 10px; }
273
- .mainbox { flex: 1 1 auto; min-width: 0; }
274
- @media (max-width: 860px) {
275
- .explorer-grid { flex-wrap: wrap; }
276
- .sidebox { flex: 1 1 100%; }
277
- }
278
-
279
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
280
- .chartbox { position: relative; }
281
- /* The chart is sized in device pixels by render() rather than scaled from a
282
- viewBox: scaling stretched the type along with the plot, and a viewBox tall
283
- enough to read made the whole panel own the screen. */
284
- .chartbox svg { display: block; }
285
- /* Chips scroll within the column so the panel height follows the chart. */
286
- .sidebox .chips { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent; }
287
- .sidebox .chips::-webkit-scrollbar { width: 9px; }
288
- .sidebox .chips::-webkit-scrollbar-thumb {
289
- background: var(--pt-muted); border-radius: 8px; border: 3px solid transparent; background-clip: content-box;
290
- }
291
- </style>
292
- </head>
293
- <body>
294
- <div class="viewbar">
295
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
296
- </div>
297
- <p class="explorer-title" id="title"></p>
298
- <div class="explorer-grid" id="grid">
299
- <div class="sidebox">
300
- <div class="controls">
301
- <label class="metricpick" id="metricpick" hidden>Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <div class="btnrow">
305
- <button class="btn" id="btn-front">Pareto front</button>
306
- <button class="btn" id="btn-all">All</button>
307
- <button class="btn" id="btn-none">Clear</button>
308
- </div>
309
- <span class="hint">Click methods or family buttons to highlight &middot; hover points for details</span>
310
- </div>
311
- <div class="chips" id="chips"></div>
312
- </div>
313
- <div class="mainbox">
314
- <div class="exportbar" id="exportbar" hidden>
315
- <span class="hint">Export figure</span>
316
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
317
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
318
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
319
- </div>
320
- <!-- Legend above the chart so readers decode the marks before the data. -->
321
- <div class="legendstrip" id="legendstrip"></div>
322
- <div class="chartbox" id="chartbox">
323
- <svg id="chart" role="img" aria-label="Pareto front explorer"></svg>
324
- <div class="tooltip"></div>
325
- </div>
326
- </div>
327
- </div>
328
- <details class="datatable">
329
- <summary>Data table</summary>
330
- <div class="tblwrap" id="tblwrap"></div>
331
- </details>
332
-
333
- <script>
334
- (function () {
335
- "use strict";
336
- const CONFIG = {"mode": "scatter", "title": null, "metrics": [{"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": ""}], "xAxes": [{"key": "x_infer", "label": "Inference time", "axisLabel": "Inference time per 1K samples (s), median \u2014 log scale", "short": "Inference (s/1K, median)"}], "chipsSide": "left"};
337
- const POINTS = [{"method":"CatBoost","family":"Tree-based","x_infer":0.0102827644,"imp":9.0952095474,"elo":1575.9,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"CatBoost","family":"Tree-based","x_infer":0.0196975147,"imp":9.1399516875,"elo":1595.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"CatBoost","family":"Tree-based","x_infer":0.2485703935,"imp":8.9317687118,"elo":1610.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0225370945,"imp":13.175735213,"elo":1418.1,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0265970701,"imp":12.1456953066,"elo":1469.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.2967122121,"imp":11.6842157983,"elo":1517.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"EBM","family":"Tree-based","x_infer":0.0054230738,"imp":17.8697973966,"elo":1220.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"EBM","family":"Tree-based","x_infer":0.0061458745,"imp":16.4411619269,"elo":1265.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"EBM","family":"Tree-based","x_infer":0.0851920607,"imp":15.5948171656,"elo":1325.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.0733045085,"imp":22.7894839202,"elo":976.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.0153132761,"imp":16.3030865094,"elo":1173.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.1990111469,"imp":15.3042054388,"elo":1216.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.1111866275,"imp":22.8643090483,"elo":1031.1,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.1141755222,"imp":19.2937753734,"elo":1101.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"FastaiMLP","family":"Neural Network","x_infer":1.6581625315,"imp":17.1916474397,"elo":1200.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LightGBM","family":"Tree-based","x_infer":0.0447579838,"imp":14.819832856,"elo":1258.1,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"LightGBM","family":"Tree-based","x_infer":0.1322357268,"imp":11.6033224024,"elo":1506.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"LightGBM","family":"Tree-based","x_infer":2.638673831,"imp":10.4658385154,"elo":1588.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LimiX","family":"Foundation Model","x_infer":5.4085730775,"imp":16.4177044552,"elo":1279.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.2496013467,"imp":14.3302915146,"elo":1297.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.3490410688,"imp":11.2147479695,"elo":1472.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ModernNCA","family":"Neural Network","x_infer":6.0133340655,"imp":9.0700293864,"elo":1614.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.0149545776,"imp":29.4235354386,"elo":900.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.1995710638,"imp":22.7471582873,"elo":1059.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.5149235805,"imp":21.6330567052,"elo":1109.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RandomForest","family":"Tree-based","x_infer":0.020899149,"imp":21.1973502954,"elo":1000.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RandomForest","family":"Tree-based","x_infer":0.0243850814,"imp":15.7438782463,"elo":1214.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RandomForest","family":"Tree-based","x_infer":0.3407391303,"imp":15.0995978866,"elo":1244.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealMLP","family":"Neural Network","x_infer":0.234981848,"imp":15.0910499881,"elo":1271.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealMLP","family":"Neural Network","x_infer":0.0807033173,"imp":10.103937795,"elo":1533.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealMLP","family":"Neural Network","x_infer":1.6746674551,"imp":8.9170097602,"elo":1625.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":0.6449695981,"imp":10.077321119,"elo":1530.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":1.3859818406,"imp":9.2178209823,"elo":1600.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":11.7359861516,"imp":8.5761380952,"elo":1645.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"SAP-RPT-OSS","family":"Foundation Model","x_infer":10.4162023366,"imp":16.9330805211,"elo":1200.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":43.7378122226,"imp":15.3106058247,"elo":1331.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":42.6414441586,"imp":12.6616447819,"elo":1403.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabDPT","family":"Foundation Model","x_infer":444.5439481393,"imp":10.2874153003,"elo":1522.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabDPT-Turbo","family":"Foundation Model","x_infer":0.1795394575,"imp":10.8085465808,"elo":1526.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabFM","family":"Foundation Model","x_infer":13.1188916889,"imp":2.6827874305,"elo":1985.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabICLv2","family":"Foundation Model","x_infer":0.1186281919,"imp":6.8505424386,"elo":1697.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.0470610966,"imp":13.9816848431,"elo":1325.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.0538978015,"imp":11.0894895039,"elo":1480.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabM","family":"Neural Network","x_infer":0.5790353938,"imp":10.2538835957,"elo":1543.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabPFN-2.6","family":"Foundation Model","x_infer":0.7008750147,"imp":7.8460750331,"elo":1722.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabPFN-3","family":"Foundation Model","x_infer":0.2147265792,"imp":5.8782765424,"elo":1793.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":1.9637818715,"imp":30.6853633341,"elo":847.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":1.723789106,"imp":25.807196939,"elo":987.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabSTAR","family":"Foundation Model","x_infer":5.282666633,"imp":25.7806935994,"elo":986.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabSwift","family":"Foundation Model","x_infer":0.1271670825,"imp":13.3366678662,"elo":1344.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.0388490278,"imp":18.1452782924,"elo":1126.1,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.050592863,"imp":14.1135657835,"elo":1272.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TorchMLP","family":"Neural Network","x_infer":1.0691781292,"imp":12.4495254577,"elo":1376.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"XGBoost","family":"Tree-based","x_infer":0.0534163689,"imp":12.9074172378,"elo":1371.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"XGBoost","family":"Tree-based","x_infer":0.0664942176,"imp":11.1023313966,"elo":1501.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"XGBoost","family":"Tree-based","x_infer":0.5626253891,"imp":10.5460036421,"elo":1548.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"iLTM","family":"Foundation Model","x_infer":9.6204056883,"imp":18.4877413347,"elo":1179.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"iLTM","family":"Foundation Model","x_infer":10.8813375711,"imp":12.1121479202,"elo":1422.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"iLTM","family":"Foundation Model","x_infer":65.1951531214,"imp":10.2205385819,"elo":1558.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"xRFM","family":"Other","x_infer":0.1448756257,"imp":25.9796305322,"elo":1046.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"xRFM","family":"Other","x_infer":0.0599221336,"imp":13.9821871779,"elo":1373.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"xRFM","family":"Other","x_infer":0.9511369135,"imp":12.4822741841,"elo":1423.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."}];
338
-
339
-
340
- const NS = "http://www.w3.org/2000/svg";
341
- // Baseline and Other are one bucket, as in the site's own type legend: they
342
- // already share a color, and each holds only a handful of methods.
343
- const FAM_MERGED = "Baseline / Other";
344
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
345
- // Normalized up front so every later lookup — colors, chips, sorting — sees
346
- // the merged family. Both templates declare POINTS above this block.
347
- for (const p of POINTS) p.family = famOf(p.family);
348
-
349
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
350
- const FAM_VAR = {
351
- "Foundation Model": "var(--fam-foundation)",
352
- "Tree-based": "var(--fam-tree)",
353
- "Neural Network": "var(--fam-nn)",
354
- "Reference Pipeline": "var(--fam-reference)",
355
- [FAM_MERGED]: "var(--fam-baseline)",
356
- };
357
- // The symbol the website shows for each family, so a family chip here reads the
358
- // same as the Type column on the site. Baseline and Other are one bucket, so
359
- // that chip carries both symbols.
360
- const FAM_SYMBOL = {
361
- "Foundation Model": "🧠⚡",
362
- "Tree-based": "🌳",
363
- "Neural Network": "🧠🔁",
364
- "Reference Pipeline": "📊",
365
- [FAM_MERGED]: "📏 ❓",
366
- };
367
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
368
- const VARIANT_VAR = {
369
- "Default": "var(--var-default)",
370
- "Tuned": "var(--var-tuned)",
371
- "Tuned + Ens.": "var(--var-tunedens)",
372
- };
373
-
374
- // A family chip's label: its symbol, its name and how many methods it holds.
375
- function famChipLabel(family, count) {
376
- const symbol = FAM_SYMBOL[family];
377
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
378
- family + ' <span class="count">&times;' + count + "</span>";
379
- }
380
-
381
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
382
- const FAM_INK = {
383
- "Foundation Model": "var(--fam-foundation-ink)",
384
- "Tree-based": "var(--fam-tree-ink)",
385
- "Neural Network": "var(--fam-nn-ink)",
386
- "Reference Pipeline": "var(--fam-reference-ink)",
387
- [FAM_MERGED]: "var(--fam-baseline-ink)",
388
- };
389
-
390
- // Create an SVG element with attributes, optionally appended to `parent`.
391
- function el(name, attrs, parent) {
392
- const node = document.createElementNS(NS, name);
393
- for (const k in attrs) node.setAttribute(k, attrs[k]);
394
- if (parent) parent.appendChild(node);
395
- return node;
396
- }
397
-
398
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
399
- // locale-independent by definition, which is the point: `toLocaleString`
400
- // would follow the *viewer's* browser locale and print 1234,5 for a German
401
- // visitor, disagreeing with the figures and CSVs beside it.
402
- function fmtNum(v, decimals) {
403
- if (v == null || !isFinite(v)) return "—";
404
- return v.toFixed(decimals);
405
- }
406
-
407
- function fmtMetric(metric, v) {
408
- if (v == null || !isFinite(v)) return "—";
409
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
410
- }
411
-
412
- function fmtTime(v) {
413
- if (v >= 100) return fmtNum(v, 0) + " s";
414
- if (v >= 1) return fmtNum(v, 1) + " s";
415
- if (v >= 0.1) return fmtNum(v, 2) + " s";
416
- return fmtNum(v, 3) + " s";
417
- }
418
-
419
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
420
- function niceStep(raw) {
421
- if (!(raw > 0)) return 1;
422
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
423
- for (const m of [1, 2, 2.5, 5, 10]) {
424
- if (mag * m >= raw) return mag * m;
425
- }
426
- return mag * 10;
427
- }
428
-
429
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
430
- // index rather than by accumulation so fractional steps do not drift.
431
- function ticks(min, max, target) {
432
- const step = niceStep((max - min) / target);
433
- const first = Math.ceil(min / step);
434
- const out = [];
435
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
436
- return out.length ? out : [min];
437
- }
438
-
439
- // A hover tooltip anchored inside `box` (which must be position:relative).
440
- function makeTooltip(box) {
441
- const node = box.querySelector(".tooltip");
442
- return {
443
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
444
- move(ev) {
445
- const r = box.getBoundingClientRect();
446
- let tx = ev.clientX - r.left + 14;
447
- const ty = ev.clientY - r.top + 12;
448
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
449
- node.style.left = tx + "px";
450
- node.style.top = ty + "px";
451
- },
452
- hide() { node.style.display = "none"; },
453
- };
454
- }
455
-
456
- // Paper view — white surface, legend + chart only — is the *default*: what a
457
- // reader wants first is the figure, and it is the state worth exporting. The
458
- // controls, chip list and data table are one click away behind "Edit view".
459
- // `afterToggle` re-renders charts whose size is measured from the layout.
460
- // `options.openInPaper` (default true) decides the state the page opens in. A
461
- // chart opens as the figure; the leaderboard table opens with its controls,
462
- // since there the interaction is the point rather than scaffolding around it.
463
- function setUpPaperView(afterToggle, options) {
464
- const opts = options || {};
465
- const root = document.documentElement;
466
- let hostTheme = null; // the embedding page's choice, captured on entry
467
- const btn = document.getElementById("btn-paper");
468
- const embedded = window.parent !== window;
469
-
470
- function setPaper(on) {
471
- document.body.classList.toggle("paper", on);
472
- if (on) {
473
- hostTheme = root.getAttribute("data-theme");
474
- root.setAttribute("data-theme", "light");
475
- } else if (hostTheme) {
476
- root.setAttribute("data-theme", hostTheme);
477
- } else {
478
- root.removeAttribute("data-theme");
479
- }
480
- btn.textContent = on ? "Edit view" : "Paper view";
481
- document.getElementById("exportbar").hidden = !on || embedded;
482
- if (afterToggle) requestAnimationFrame(afterToggle);
483
- postHeight();
484
- }
485
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
486
- // Embedded, the host page owns these controls — they sit beside the panel's
487
- // static-figure toggle and are driven from the outside. Standalone (the
488
- // shareable single file) this page needs its own.
489
- if (embedded) document.querySelector(".viewbar").hidden = true;
490
- window.addEventListener("message", ev => {
491
- const d = ev.data;
492
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
493
- });
494
- // Only standalone: embedded, the host owns the button and would not see the
495
- // key press, so its label would fall out of step with the frame.
496
- if (!embedded) {
497
- document.addEventListener("keydown", ev => {
498
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
499
- });
500
- }
501
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
502
- }
503
-
504
- // --- Figure export ---------------------------------------------------------
505
- // The chart is live SVG, so a file can be built from it directly. Three things
506
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
507
- // nothing outside this document), it has no background or font of its own, and
508
- // the legend is HTML rather than part of the SVG.
509
-
510
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
511
- // cloned and its label re-emitted at the measured position. foreignObject would
512
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
513
- // break the PNG path.
514
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
515
- // once the node leaves this document.
516
- function resolveVars(root, resolve) {
517
- for (const node of [root, ...root.querySelectorAll("*")]) {
518
- for (const attr of ["fill", "stroke"]) {
519
- const value = node.getAttribute(attr);
520
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
521
- }
522
- }
523
- }
524
-
525
- function legendToSvg(container, resolve) {
526
- const base = container.getBoundingClientRect();
527
- const group = document.createElementNS(NS, "g");
528
- let height = 0;
529
- for (const item of container.querySelectorAll(".item")) {
530
- const box = item.getBoundingClientRect();
531
- if (!box.width) continue;
532
- height = Math.max(height, box.bottom - base.top);
533
- let textLeft = box.left - base.left;
534
- const glyph = item.querySelector("svg");
535
- if (glyph) {
536
- const gbox = glyph.getBoundingClientRect();
537
- const wrap = el("g", {
538
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
539
- }, group);
540
- const glyphClone = glyph.cloneNode(true);
541
- resolveVars(glyphClone, resolve);
542
- wrap.appendChild(glyphClone);
543
- textLeft = gbox.right - base.left + 5;
544
- }
545
- const label = item.textContent.trim();
546
- if (!label) continue;
547
- const colored = item.querySelector("[style*='color']");
548
- const text = el("text", {
549
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
550
- fill: resolve(getComputedStyle(colored || item).color),
551
- }, group);
552
- text.textContent = label;
553
- }
554
- return { group, height: Math.ceil(height) };
555
- }
556
-
557
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
558
- // y-axis beside the scrolling plot) still exports as one figure.
559
- function buildExportSvg(parts, legendEl, pad = 10) {
560
- const rootStyle = getComputedStyle(document.documentElement);
561
- const resolve = value => String(value).replace(
562
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
563
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
564
-
565
- let chartW = 0, chartH = 0;
566
- for (const part of parts) {
567
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
568
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
569
- }
570
-
571
- const out = document.createElementNS(NS, "svg");
572
- out.setAttribute("xmlns", NS);
573
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
574
- let top = pad;
575
- const later = []; // built after the width is known
576
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
577
- if (legend && legend.height) {
578
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
579
- later.push(() => out.appendChild(legend.group));
580
- top += legend.height + 8;
581
- }
582
-
583
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
584
- const height = top + chartH + pad;
585
- out.setAttribute("width", Math.ceil(width));
586
- out.setAttribute("height", Math.ceil(height));
587
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
588
- for (const build of later) build();
589
-
590
- for (const part of parts) {
591
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
592
- const clone = part.svg.cloneNode(true);
593
- resolveVars(clone, resolve);
594
- while (clone.firstChild) group.appendChild(clone.firstChild);
595
- }
596
- return out;
597
- }
598
-
599
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
600
- function slugify(text) {
601
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
602
- }
603
-
604
- function downloadUrl(url, filename) {
605
- const a = document.createElement("a");
606
- a.href = url;
607
- a.download = filename;
608
- document.body.appendChild(a);
609
- a.click();
610
- a.remove();
611
- }
612
-
613
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
614
- function rasterize(svg, scale, done, fail) {
615
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
616
- const img = new Image();
617
- img.onload = () => {
618
- const canvas = document.createElement("canvas");
619
- canvas.width = Math.round(width * scale);
620
- canvas.height = Math.round(height * scale);
621
- const ctx = canvas.getContext("2d");
622
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
623
- ctx.drawImage(img, 0, 0);
624
- done(canvas, width, height);
625
- };
626
- img.onerror = fail;
627
- img.src = "data:image/svg+xml;charset=utf-8,"
628
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
629
- }
630
-
631
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
632
- // cost this page its zero-dependency, single-file property. The image is stored
633
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
634
- // in points to the figure's CSS size, so it prints at the size it appears here
635
- // and the pixels land at 96*scale dpi.
636
- async function buildPdf(canvas, cssWidth, cssHeight) {
637
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
638
- const rgb = new Uint8Array((pixels.length / 4) * 3);
639
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
640
- rgb[j] = pixels[i];
641
- rgb[j + 1] = pixels[i + 1];
642
- rgb[j + 2] = pixels[i + 2];
643
- }
644
- const deflated = new Uint8Array(await new Response(
645
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
646
-
647
- const encoder = new TextEncoder();
648
- const chunks = [];
649
- const offsets = [];
650
- let cursor = 0;
651
- const put = data => {
652
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
653
- chunks.push(bytes);
654
- cursor += bytes.length;
655
- };
656
- const object = (id, body, stream) => {
657
- offsets[id] = cursor;
658
- put(`${id} 0 obj\n${body}\n`);
659
- if (stream) {
660
- put("stream\n");
661
- put(stream);
662
- put("\nendstream\n");
663
- }
664
- put("endobj\n");
665
- };
666
-
667
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
668
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
669
- put("%PDF-1.4\n");
670
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
671
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
672
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
673
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
674
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
675
- object(4, "<< /Type /XObject /Subtype /Image "
676
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
677
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
678
- object(5, `<< /Length ${content.length} >>`, content);
679
-
680
- const xref = cursor;
681
- let table = "xref\n0 6\n0000000000 65535 f \n";
682
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
683
- put(table);
684
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
685
- return new Blob(chunks, { type: "application/pdf" });
686
- }
687
-
688
- // Wire up the export buttons; `getParts` is called per click so the file always
689
- // matches what is on screen. Returns a `run(format)` the host page can drive.
690
- function setUpExport(getParts, basename) {
691
- const buttons = {
692
- svg: document.getElementById("btn-svg"),
693
- png: document.getElementById("btn-png"),
694
- pdf: document.getElementById("btn-pdf"),
695
- };
696
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
697
-
698
- // A sandboxed frame has no modals, so a failure is reported on the button.
699
- function complain(format) {
700
- const button = buttons[format];
701
- if (!button) return;
702
- const label = button.textContent;
703
- button.textContent = "failed";
704
- setTimeout(() => { button.textContent = label; }, 2500);
705
- }
706
-
707
- function run(format) {
708
- const svg = figure();
709
- const name = basename();
710
- if (format === "svg") {
711
- downloadUrl("data:image/svg+xml;charset=utf-8,"
712
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
713
- return;
714
- }
715
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
716
- // points so the pixels already land near 200 dpi at print size.
717
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
718
- if (format === "png") {
719
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
720
- } else {
721
- buildPdf(canvas, cssWidth, cssHeight)
722
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
723
- .catch(() => complain("pdf"));
724
- }
725
- }, () => complain(format));
726
- }
727
-
728
- for (const format of Object.keys(buttons)) {
729
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
730
- }
731
- // Embedded, the buttons live in the host's panel header (see main.taExport).
732
- window.addEventListener("message", ev => {
733
- const d = ev.data;
734
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
735
- });
736
- }
737
-
738
- // When embedded, report the content height so the host page can size the
739
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
740
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
741
- // clamped to at least the iframe's current viewport, which turns the
742
- // resize round-trip into a grow-forever feedback loop. The change guard
743
- // stops re-posting once the height settles.
744
- let lastPostedHeight = 0;
745
- function postHeight() {
746
- if (window.parent === window) return;
747
- const height = Math.ceil(document.body.offsetHeight);
748
- if (Math.abs(height - lastPostedHeight) < 3) return;
749
- lastPostedHeight = height;
750
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
751
- }
752
-
753
-
754
- const TRAJECTORY = CONFIG.mode === "trajectory";
755
-
756
- const titleEl = document.getElementById("title");
757
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
758
-
759
- // Column order: chips/controls left of the chart, or mirrored.
760
- document.getElementById("grid").classList.add(CONFIG.chipsSide === "right" ? "chips-right" : "chips-left");
761
-
762
- const svg = document.getElementById("chart");
763
- const box = document.getElementById("chartbox");
764
- const chipsBox = document.getElementById("chips");
765
- const controlsBox = document.querySelector(".controls");
766
- const tip = makeTooltip(box);
767
-
768
- // Marker glyph per variant at (cx, cy); trajectories use circles everywhere.
769
- function drawMark(parent, cx, cy, variant, color, size, opacity, dataM, whiteStroke) {
770
- const common = { opacity: opacity, "data-m": dataM };
771
- let node;
772
- if (TRAJECTORY || variant === "Default" || !variant) {
773
- node = el("circle", { ...common, cx, cy, r: size, fill: color }, parent);
774
- } else if (variant === "Tuned") {
775
- const s = size * 1.75;
776
- node = el("rect", { ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1.5, fill: color }, parent);
777
- } else if (variant === "Tuned + Ens.") {
778
- const d = size * 0.95;
779
- node = el("path", {
780
- ...common,
781
- d: `M${cx - d},${cy - d} L${cx + d},${cy + d} M${cx - d},${cy + d} L${cx + d},${cy - d}`,
782
- stroke: color, "stroke-width": size * 0.62, fill: "none", "stroke-linecap": "round",
783
- }, parent);
784
- } else {
785
- // Any other variant (e.g. "Baseline", holdout types): diamond.
786
- const s = size * 1.45;
787
- node = el("rect", {
788
- ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1,
789
- fill: color, transform: `rotate(45 ${cx} ${cy})`,
790
- }, parent);
791
- }
792
- if (whiteStroke && variant !== "Tuned + Ens.") {
793
- node.setAttribute("stroke", "var(--card)");
794
- node.setAttribute("stroke-width", "1");
795
- }
796
- return node;
797
- }
798
-
799
- function drawImputedRing(parent, cx, cy, size, color, opacity, dataM) {
800
- el("circle", {
801
- cx, cy, r: size + 4.5, fill: "none", stroke: color, "stroke-width": 1.4,
802
- "stroke-dasharray": "3 2.5", opacity: opacity, "data-m": dataM,
803
- }, parent);
804
- }
805
-
806
- // Fat green arrow pointing into the optimal corner (mirrors the static
807
- // figures' "Optimal" arrow so both read the same way).
808
- function drawOptimalArrow(parent, lowerBetter, M, W, H) {
809
- const cornerY = lowerBetter ? H - M.b - 12 : M.t + 12;
810
- const tailY = lowerBetter ? H - M.b - 64 : M.t + 64;
811
- const cx = M.l + 12, tx = M.l + 64;
812
- const dx = cx - tx, dy = cornerY - tailY;
813
- const len = Math.hypot(dx, dy);
814
- const ux = dx / len, uy = dy / len;
815
- const headLen = 16;
816
- const bx = cx - ux * headLen, by = cornerY - uy * headLen; // head base center
817
- // line stops at the head base
818
- el("line", {
819
- x1: tx, y1: tailY, x2: bx, y2: by,
820
- stroke: "var(--optimal)", "stroke-width": 13, "stroke-linecap": "round", opacity: 0.92,
821
- }, parent);
822
- const px = -uy, py = ux; // perpendicular
823
- el("polygon", {
824
- points: `${cx},${cornerY} ${bx + px * 11},${by + py * 11} ${bx - px * 11},${by - py * 11}`,
825
- fill: "var(--optimal)", opacity: 0.92,
826
- }, parent);
827
- let angle = Math.atan2(dy, dx) * 180 / Math.PI;
828
- if (angle > 90 || angle < -90) angle += 180;
829
- const mx = (tx + bx) / 2, my = (tailY + by) / 2;
830
- const t = el("text", {
831
- x: mx, y: my, "text-anchor": "middle", "dominant-baseline": "middle",
832
- "font-size": 10.5, "font-weight": 700, fill: "#ffffff",
833
- transform: `rotate(${angle} ${mx} ${my})`,
834
- }, parent);
835
- t.textContent = "Optimal";
836
- }
837
-
838
- // ---------- data ----------
839
- const byMethod = new Map();
840
- for (const p of POINTS) {
841
- if (!byMethod.has(p.method)) byMethod.set(p.method, []);
842
- byMethod.get(p.method).push(p); // insertion order = builder's point order
843
- }
844
-
845
- const METRICS = CONFIG.metrics;
846
- let metricKey = METRICS[0].key;
847
- const metricByKey = {};
848
- for (const m of METRICS) metricByKey[m.key] = m;
849
-
850
- // Single time axis per explorer (the scatter ships inference time, the
851
- // trajectories train time).
852
- const X_AXIS = CONFIG.xAxes[0];
853
- const xKey = X_AXIS.key;
854
-
855
- function mval(p, metric) { return p[metric.key]; }
856
-
857
- function computeFront(metric) {
858
- const xk = xKey;
859
- const pts = [...POINTS].sort((a, b) =>
860
- a[xk] - b[xk] || (metric.lowerBetter ? mval(a, metric) - mval(b, metric) : mval(b, metric) - mval(a, metric)));
861
- const verts = [];
862
- const methods = new Set();
863
- let best = null;
864
- for (const p of pts) {
865
- const v = mval(p, metric);
866
- if (best === null || (metric.lowerBetter ? v < best : v > best)) {
867
- if (best !== null) verts.push([p[xk], best]);
868
- verts.push([p[xk], v]);
869
- best = v;
870
- methods.add(p.method);
871
- }
872
- }
873
- return { verts, methods };
874
- }
875
-
876
- const state = { active: new Set(computeFront(metricByKey[metricKey]).methods) };
877
-
878
- // ---------- chart ----------
879
- // A flat, fixed-height plot: two of these panels then fit on one screen.
880
- const CHART_H = 400;
881
- const M = { l: 62, r: 18, t: 14, b: 52 };
882
-
883
- function render() {
884
- const metric = metricByKey[metricKey];
885
- svg.textContent = "";
886
- const W = Math.max(360, Math.round(box.clientWidth));
887
- const H = CHART_H;
888
- svg.setAttribute("width", W);
889
- svg.setAttribute("height", H);
890
- // Keep the chip list from outgrowing the chart beside it.
891
- chipsBox.style.maxHeight = Math.max(170, H - controlsBox.offsetHeight + 20) + "px";
892
-
893
- // x scale (log)
894
- const xsAll = POINTS.map(p => p[xKey]);
895
- const xmin = Math.min(...xsAll) * 0.65, xmax = Math.max(...xsAll) * 1.6;
896
- const lx0 = Math.log10(xmin), lx1 = Math.log10(xmax);
897
- const X = v => M.l + (Math.log10(v) - lx0) / (lx1 - lx0) * (W - M.l - M.r);
898
-
899
- const vals = POINTS.map(p => mval(p, metric));
900
- let y0, y1;
901
- if (metric.fromZero) {
902
- y0 = 0; y1 = Math.max(...vals) * 1.07;
903
- } else {
904
- const pad = (Math.max(...vals) - Math.min(...vals)) * 0.07;
905
- y0 = Math.min(...vals) - pad; y1 = Math.max(...vals) + pad;
906
- }
907
- const Y = v => M.t + (1 - (v - y0) / (y1 - y0)) * (H - M.t - M.b);
908
-
909
- // grid + axes
910
- const grid = el("g", {}, svg);
911
- for (let e = Math.ceil(lx0); Math.pow(10, e) < xmax; e++) {
912
- const gx = X(Math.pow(10, e));
913
- el("line", { x1: gx, y1: M.t, x2: gx, y2: H - M.b, stroke: "var(--line)", "stroke-width": 1 }, grid);
914
- const lbl = fmtNum(Math.pow(10, e), e >= 0 ? 0 : -e);
915
- el("text", { x: gx, y: H - M.b + 20, "text-anchor": "middle", "font-size": 12.5, fill: "var(--muted)" }, grid)
916
- .textContent = lbl;
917
- }
918
- for (const yv of ticks(y0, y1, 6)) {
919
- const gy = Y(yv);
920
- el("line", { x1: M.l, y1: gy, x2: W - M.r, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
921
- el("text", { x: M.l - 8, y: gy + 4, "text-anchor": "end", "font-size": 12.5, fill: "var(--muted)" }, grid)
922
- .textContent = fmtNum(yv, Number.isInteger(yv) ? 0 : metric.decimals);
923
- }
924
- el("rect", { x: M.l, y: M.t, width: W - M.l - M.r, height: H - M.t - M.b, fill: "none", stroke: "var(--line)" }, grid);
925
- el("text", {
926
- x: (M.l + W - M.r) / 2, y: H - 10, "text-anchor": "middle", "font-size": 14,
927
- "font-weight": 650, fill: "var(--ink)",
928
- }, grid).textContent = X_AXIS.axisLabel;
929
- el("text", {
930
- x: 0, y: 0, "text-anchor": "middle", "font-size": 14, "font-weight": 650, fill: "var(--ink)",
931
- transform: `translate(16 ${(M.t + H - M.b) / 2}) rotate(-90)`,
932
- }, grid).textContent = metric.axisLabel;
933
-
934
- drawOptimalArrow(grid, metric.lowerBetter, M, W, H);
935
-
936
- // pareto front (always shown)
937
- const front = computeFront(metric);
938
- const fv = front.verts;
939
- if (fv.length) {
940
- let d = `M${X(fv[0][0])},${metric.lowerBetter ? M.t : H - M.b}`;
941
- for (const [fx, fy] of fv) d += ` L${X(fx)},${Y(fy)}`;
942
- d += ` L${W - M.r},${Y(fv[fv.length - 1][1])}`;
943
- el("path", { d, fill: "none", stroke: "var(--ink)", "stroke-width": 1.6, "stroke-dasharray": "7 5", opacity: 0.85 }, svg);
944
- }
945
-
946
- const isOn = m => state.active.has(m);
947
-
948
- // connectors: variant links (scatter) / the trajectory itself
949
- const conn = el("g", {}, svg);
950
- for (const [method, pts] of byMethod) {
951
- if (pts.length < 2) continue;
952
- const on = isOn(method);
953
- if (!TRAJECTORY && !on) continue; // scatter: connectors only for active methods
954
- const dd = pts.map((p, i) => `${i ? "L" : "M"}${X(p[xKey])},${Y(mval(p, metric))}`).join(" ");
955
- el("path", {
956
- d: dd, fill: "none",
957
- stroke: on ? FAM_VAR[pts[0].family] : "var(--pt-muted)",
958
- "stroke-width": on ? (TRAJECTORY ? 2 : 1.4) : 1,
959
- opacity: on ? 0.6 : 0.35,
960
- "data-m": method,
961
- }, conn);
962
- }
963
-
964
- // points: inactive first, active on top
965
- const ptsOff = el("g", {}, svg);
966
- const ptsOn = el("g", {}, svg);
967
- for (const [method, pts] of byMethod) {
968
- const on = isOn(method);
969
- for (const p of pts) {
970
- const color = on ? FAM_VAR[p.family] : "var(--pt-muted)";
971
- const size = (on ? 7 : 5) * (TRAJECTORY ? 0.8 : 1);
972
- const op = on ? 0.95 : 0.5;
973
- drawMark(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), p.variant, color, size, op, p.method, on);
974
- // Imputation ring: every affected point in scatter mode; only the
975
- // trajectory's end point in trajectory mode (a ring on all ~8 line
976
- // points would read as beads, and the chip's ‡ already flags the line).
977
- if (p.imputed && (!TRAJECTORY || p === pts[pts.length - 1])) {
978
- drawImputedRing(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), size, color, op, p.method);
979
- }
980
- }
981
- }
982
-
983
- // labels for active methods at their best point, greedy de-overlap
984
- const labels = [];
985
- for (const [method, pts] of byMethod) {
986
- if (!isOn(method)) continue;
987
- const best = pts.reduce((a, b) =>
988
- (metric.lowerBetter ? mval(a, metric) < mval(b, metric) : mval(a, metric) > mval(b, metric)) ? a : b);
989
- labels.push({ method, family: best.family, x: X(best[xKey]) + 10, y: Y(mval(best, metric)) - 10 });
990
- }
991
- labels.sort((a, b) => a.y - b.y);
992
- for (let i = 1; i < labels.length; i++) {
993
- for (let j = 0; j < i; j++) {
994
- if (Math.abs(labels[i].x - labels[j].x) < 110 && Math.abs(labels[i].y - labels[j].y) < 15) {
995
- labels[i].y = labels[j].y + 15;
996
- }
997
- }
998
- }
999
- const lg = el("g", {}, svg);
1000
- for (const l of labels) {
1001
- const t = el("text", {
1002
- x: Math.min(l.x, W - M.r - 8), y: Math.max(l.y, M.t + 12), "font-size": 13, "font-weight": 700,
1003
- fill: FAM_VAR[l.family], "paint-order": "stroke", stroke: "var(--card)", "stroke-width": 3.5,
1004
- "text-anchor": l.x > W - 120 ? "end" : "start",
1005
- }, lg);
1006
- t.textContent = l.method;
1007
- }
1008
-
1009
- // invisible hit targets on top (bigger than marks)
1010
- const hits = el("g", {}, svg);
1011
- for (const p of POINTS) {
1012
- const h = el("circle", { cx: X(p[xKey]), cy: Y(mval(p, metric)), r: 12, fill: "transparent", cursor: "pointer" }, hits);
1013
- h.addEventListener("mouseenter", ev => showTip(p, ev));
1014
- h.addEventListener("mousemove", ev => tip.move(ev));
1015
- h.addEventListener("mouseleave", () => hideTip(p.method));
1016
- h.addEventListener("click", () => toggle(p.method));
1017
- }
1018
- }
1019
-
1020
- // Temporary hover emphasis without a re-render (a re-render would replace
1021
- // the hit node under the cursor mid-hover).
1022
- function emphasize(method, on) {
1023
- svg.querySelectorAll(`[data-m="${CSS.escape(method)}"]`).forEach(n => {
1024
- if (on) {
1025
- if (!n.dataset.save) n.dataset.save = n.getAttribute("opacity") || "1";
1026
- n.setAttribute("opacity", "0.95");
1027
- } else if (n.dataset.save) {
1028
- n.setAttribute("opacity", n.dataset.save);
1029
- delete n.dataset.save;
1030
- }
1031
- });
1032
- }
1033
-
1034
- function showTip(p, ev) {
1035
- emphasize(p.method, true);
1036
- const sub = TRAJECTORY ? (p.n_configs != null ? `${p.n_configs} configs` : "") : (p.variant || "");
1037
- let html = `<div class="t-name">${p.method}` + (sub ? ` <span class="t-var">(${sub})</span>` : "") + "</div>" +
1038
- `<div>${p.family}</div>`;
1039
- for (const m of METRICS) {
1040
- html += `<div>${m.label}: <b>${fmtMetric(m, mval(p, m))}</b></div>`;
1041
- }
1042
- html += `<div>${X_AXIS.short}: <b>${fmtTime(p[xKey])}</b></div>`;
1043
- if (p.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(p.imputed_pct, 0)}% of datasets</div>`;
1044
- tip.show(html, ev);
1045
- }
1046
- function hideTip(method) {
1047
- emphasize(method, false);
1048
- tip.hide();
1049
- }
1050
-
1051
- // ---------- chips ----------
1052
- const chipByMethod = new Map();
1053
- const famChips = new Map();
1054
- function familyMethods(fam) {
1055
- return [...byMethod.keys()].filter(m => byMethod.get(m)[0].family === fam);
1056
- }
1057
- // Chips are listed by leaderboard rank — best Elo first when Elo is
1058
- // configured, otherwise best value of the primary metric.
1059
- const RANK_METRIC = metricByKey["elo"] || metricByKey[METRICS[0].key];
1060
- function bestVal(method) {
1061
- const vals = byMethod.get(method).map(p => mval(p, RANK_METRIC));
1062
- return RANK_METRIC.lowerBetter ? Math.min(...vals) : Math.max(...vals);
1063
- }
1064
- function rankSorted(methods) {
1065
- return [...methods].sort((a, b) =>
1066
- RANK_METRIC.lowerBetter ? bestVal(a) - bestVal(b) : bestVal(b) - bestVal(a));
1067
- }
1068
- function buildChips() {
1069
- for (const fam of FAM_ORDER) {
1070
- const methods = rankSorted(familyMethods(fam));
1071
- if (!methods.length) continue;
1072
- const row = document.createElement("div");
1073
- row.className = "chiprow";
1074
- const famBtn = document.createElement("button");
1075
- famBtn.className = "famchip";
1076
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1077
- famBtn.innerHTML = famChipLabel(fam, methods.length);
1078
- famBtn.title = `Toggle all ${methods.length} ${fam} methods`;
1079
- famBtn.addEventListener("click", () => toggleFamily(fam));
1080
- row.appendChild(famBtn);
1081
- famChips.set(fam, famBtn);
1082
- const set = document.createElement("div");
1083
- set.className = "chipset";
1084
- for (const m of methods) {
1085
- const b = document.createElement("button");
1086
- b.className = "chip";
1087
- b.style.setProperty("--fam", FAM_VAR[fam]);
1088
- const imputed = byMethod.get(m).some(p => p.imputed);
1089
- const label = document.createElement("span");
1090
- label.textContent = m;
1091
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1092
- b.appendChild(label);
1093
- if (imputed) {
1094
- const mark = document.createElement("span");
1095
- mark.className = "imp-mark";
1096
- mark.textContent = "‡";
1097
- b.appendChild(mark);
1098
- }
1099
- b.title = m + (imputed ? " — partially imputed" : "");
1100
- b.addEventListener("click", () => toggle(m));
1101
- set.appendChild(b);
1102
- chipByMethod.set(m, b);
1103
- }
1104
- row.appendChild(set);
1105
- chipsBox.appendChild(row);
1106
- }
1107
- }
1108
- function syncChips() {
1109
- for (const [m, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.active.has(m)));
1110
- for (const [fam, b] of famChips) {
1111
- b.setAttribute("aria-pressed", String(familyMethods(fam).every(m => state.active.has(m))));
1112
- }
1113
- }
1114
- function toggle(m) {
1115
- if (state.active.has(m)) state.active.delete(m); else state.active.add(m);
1116
- syncChips();
1117
- render();
1118
- }
1119
- function toggleFamily(fam) {
1120
- const methods = familyMethods(fam);
1121
- const allOn = methods.every(m => state.active.has(m));
1122
- for (const m of methods) {
1123
- if (allOn) state.active.delete(m); else state.active.add(m);
1124
- }
1125
- syncChips();
1126
- render();
1127
- }
1128
- function setActive(methods) {
1129
- state.active = new Set(methods);
1130
- syncChips();
1131
- render();
1132
- }
1133
- document.getElementById("btn-front").addEventListener("click",
1134
- () => setActive(computeFront(metricByKey[metricKey]).methods));
1135
- document.getElementById("btn-all").addEventListener("click", () => setActive([...byMethod.keys()]));
1136
- document.getElementById("btn-none").addEventListener("click", () => setActive([]));
1137
-
1138
- // metric selector (hidden when only one metric is configured)
1139
- const metricPick = document.getElementById("metricpick");
1140
- const metricSelect = document.getElementById("metric-select");
1141
- if (METRICS.length > 1) {
1142
- metricPick.hidden = false;
1143
- for (const m of METRICS) {
1144
- const opt = document.createElement("option");
1145
- opt.value = m.key;
1146
- opt.textContent = m.label;
1147
- metricSelect.appendChild(opt);
1148
- }
1149
- metricSelect.addEventListener("change", ev => {
1150
- metricKey = ev.target.value;
1151
- render();
1152
- });
1153
- }
1154
-
1155
- // ---------- legend strip ----------
1156
- function buildLegend() {
1157
- const box2 = document.getElementById("legendstrip");
1158
- let html = "";
1159
- if (!TRAJECTORY) {
1160
- html +=
1161
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><circle cx="7" cy="7" r="5" fill="var(--muted)"/></svg> Default</span>' +
1162
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" rx="1.5" fill="var(--muted)"/></svg> Tuned</span>' +
1163
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><path d="M3,3 L11,11 M3,11 L11,3" stroke="var(--muted)" stroke-width="2.6" stroke-linecap="round"/></svg> Tuned + Ensembled</span>';
1164
- } else {
1165
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--muted)" stroke-width="2"/><circle cx="6" cy="4" r="2.6" fill="var(--muted)"/><circle cx="16" cy="4" r="2.6" fill="var(--muted)"/></svg> Tuning trajectory (more configs &rarr; more time)</span>';
1166
- }
1167
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--ink)" stroke-width="1.6" stroke-dasharray="6 4"/></svg> Pareto front (always shown)</span>';
1168
- if (POINTS.some(p => p.imputed)) {
1169
- html += '<span class="item"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="4" fill="var(--muted)"/><circle cx="9" cy="9" r="7.5" fill="none" stroke="var(--muted)" stroke-width="1.3" stroke-dasharray="3 2.5"/></svg> &Dagger; partially imputed</span>';
1170
- }
1171
- // Model family, named: highlighted points are colored by family, and in paper
1172
- // view the chip list that would otherwise decode them is hidden.
1173
- const families = FAM_ORDER.filter(f => POINTS.some(p => p.family === f));
1174
- if (families.length > 1) {
1175
- html += '<span class="legendbreak"></span><span class="item">Family:</span>';
1176
- for (const fam of families) {
1177
- html += `<span class="item"><svg width="12" height="12" viewBox="0 0 12 12">` +
1178
- `<circle cx="6" cy="6" r="5" fill="${FAM_VAR[fam]}"/></svg> ` +
1179
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`;
1180
- }
1181
- }
1182
- box2.innerHTML = html;
1183
- }
1184
-
1185
- // ---------- paper view ----------
1186
- setUpPaperView(render);
1187
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(document.title));
1188
-
1189
- // ---------- data table ----------
1190
- function buildTable() {
1191
- const m0 = metricByKey[METRICS[0].key];
1192
- const rows = [...POINTS].sort((a, b) =>
1193
- m0.lowerBetter ? mval(a, m0) - mval(b, m0) : mval(b, m0) - mval(a, m0));
1194
- let html = "<table><thead><tr><th>Method</th>";
1195
- html += TRAJECTORY ? "<th>Configs</th>" : "<th>Variant</th>";
1196
- html += "<th>Family</th>";
1197
- for (const m of METRICS) html += `<th>${m.label}</th>`;
1198
- html += `<th>${X_AXIS.short}</th><th>Imputed</th></tr></thead><tbody>`;
1199
- for (const p of rows) {
1200
- html += `<tr><td>${p.method}</td><td>${TRAJECTORY ? (p.n_configs != null ? p.n_configs : "—") : p.variant}</td><td>${p.family}</td>`;
1201
- for (const m of METRICS) html += `<td>${fmtMetric(m, mval(p, m))}</td>`;
1202
- html += `<td>${fmtNum(p[xKey], 3)}</td>`;
1203
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1204
- }
1205
- html += "</tbody></table>";
1206
- document.getElementById("tblwrap").innerHTML = html;
1207
- }
1208
-
1209
- const _renderInner = render;
1210
- render = function () {
1211
- _renderInner();
1212
- postHeight();
1213
- };
1214
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1215
- let resizeTimer = null;
1216
- window.addEventListener("resize", () => {
1217
- clearTimeout(resizeTimer);
1218
- resizeTimer = setTimeout(render, 120);
1219
- });
1220
- window.addEventListener("load", postHeight);
1221
-
1222
- buildChips();
1223
- buildLegend();
1224
- buildTable();
1225
- syncChips();
1226
- render();
1227
- })();
1228
- </script>
1229
- </body>
1230
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_improvability_vs_time_infer.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:66175fc84a0e3c329e8475d6213f76ff63a8225c786eee15d60e809db5b02424
3
- size 275007
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_front_points.csv DELETED
@@ -1,63 +0,0 @@
1
- method,variant,family,x_infer,x_train,imp,elo,imputed,imputed_pct
2
- TabFM,Default,Foundation Model,13.118891688893655,27.288414010629126,2.682787430454827,1985.6,False,0.0
3
- TabPFN-3,Default,Foundation Model,0.21472657924676145,1.0099025842179519,5.8782765424085754,1793.5,False,0.0
4
- TabPFN-2.6,Default,Foundation Model,0.700875014724915,2.7558592936727737,7.846075033073932,1722.4,False,0.0
5
- TabICLv2,Default,Foundation Model,0.118628191947937,0.6015671441553114,6.850542438637394,1697.2,False,0.0
6
- RealTabPFN-2.5,Tuned + Ens.,Foundation Model,11.735986151605083,735.5848736648751,8.576138095171979,1645.8,False,0.0
7
- RealMLP,Tuned + Ens.,Neural Network,1.6746674551212202,1719.820290101634,8.917009760191236,1625.1,False,0.0
8
- ModernNCA,Tuned + Ens.,Neural Network,6.0133340654646315,2526.306680600179,9.070029386372564,1614.9,False,0.0
9
- CatBoost,Tuned + Ens.,Tree-based,0.24857039354928848,489.92741010372185,8.931768711834955,1610.6,False,0.0
10
- RealTabPFN-2.5,Tuned,Foundation Model,1.3859818405575224,735.5848736648751,9.217820982256466,1600.2,False,0.0
11
- CatBoost,Tuned,Tree-based,0.01969751471208236,489.92741010372185,9.139951687547399,1595.7,False,0.0
12
- LightGBM,Tuned + Ens.,Tree-based,2.638673830958025,131.56200707040514,10.465838515368032,1588.9,False,0.0
13
- CatBoost,Default,Tree-based,0.010282764438985344,2.469413828723084,9.095209547420893,1575.9,False,0.0
14
- iLTM,Tuned + Ens.,Foundation Model,65.19515312136267,2067.952899612129,10.22053858185718,1558.4,False,0.0
15
- XGBoost,Tuned + Ens.,Tree-based,0.5626253890991211,282.1320041804491,10.54600364212248,1548.1,False,0.0
16
- TabM,Tuned + Ens.,Neural Network,0.5790353938445546,1236.8778796591432,10.253883595670708,1543.9,False,0.0
17
- RealMLP,Tuned,Neural Network,0.08070331733080324,1719.820290101634,10.10393779502221,1533.3,False,0.0
18
- RealTabPFN-2.5,Default,Foundation Model,0.644969598113518,1.8760518371058228,10.077321118993932,1530.5,False,0.0
19
- TabDPT-Turbo,Default,Foundation Model,0.1795394574862431,0.7535798581633265,10.808546580815626,1526.5,False,0.0
20
- TabDPT,Tuned + Ens.,Foundation Model,444.5439481393411,4786.598933193933,10.287415300269947,1522.0,False,0.0
21
- ChimeraBoost,Tuned + Ens.,Tree-based,0.2967122120719018,95.91691558887598,11.684215798303551,1517.0,False,0.0
22
- LightGBM,Tuned,Tree-based,0.13223572676641904,131.56200707040514,11.603322402427677,1506.5,False,0.0
23
- XGBoost,Tuned,Tree-based,0.06649421763475445,282.1320041804491,11.102331396641715,1501.2,False,0.0
24
- TabM,Tuned,Neural Network,0.053897801486310716,1236.8778796591432,11.089489503934743,1480.7,False,0.0
25
- ModernNCA,Tuned,Neural Network,0.3490410688188341,2526.306680600179,11.214747969543462,1472.1,False,0.0
26
- ChimeraBoost,Tuned,Tree-based,0.026597070066552417,95.91691558887598,12.145695306561958,1469.6,False,0.0
27
- xRFM,Tuned + Ens.,Other,0.9511369134522893,566.736982568635,12.482274184135383,1423.2,False,0.0
28
- iLTM,Tuned,Foundation Model,10.881337571144105,2067.952899612129,12.112147920197751,1422.7,False,0.0
29
- ChimeraBoost,Default,Tree-based,0.02253709445919907,0.47897883160115473,13.175735213047773,1418.1,False,0.0
30
- TabDPT,Tuned,Foundation Model,42.64144415855408,4786.598933193933,12.661644781930676,1403.6,False,0.0
31
- TorchMLP,Tuned + Ens.,Neural Network,1.0691781291613058,1770.5620854905956,12.449525457707859,1376.8,False,0.0
32
- xRFM,Tuned,Other,0.05992213358760137,566.736982568635,13.982187177893387,1373.4,False,0.0
33
- XGBoost,Default,Tree-based,0.053416368885586527,0.4869354633908524,12.907417237764236,1371.6,False,0.0
34
- TabSwift,Default,Foundation Model,0.12716708249348388,0.5069992698737326,13.3366678662291,1344.2,False,0.0
35
- TabDPT,Default,Foundation Model,43.737812222627376,46.6209023206833,15.31060582466251,1331.5,False,0.0
36
- TabM,Default,Neural Network,0.047061096558805166,2.1306936635677576,13.981684843143066,1325.7,False,0.0
37
- EBM,Tuned + Ens.,Tree-based,0.08519206070345582,695.6092947224777,15.59481716563213,1325.5,False,0.0
38
- ModernNCA,Default,Neural Network,0.24960134666003478,8.063770877226162,14.330291514556546,1297.8,False,0.0
39
- LimiX,Default,Foundation Model,5.408573077515913,6.553292592842144,16.417704455233075,1279.3,False,0.0
40
- TorchMLP,Tuned,Neural Network,0.05059286303330543,1770.5620854905956,14.11356578349425,1272.5,False,0.0
41
- RealMLP,Default,Neural Network,0.23498184801373212,6.7532462416847245,15.091049988060348,1271.6,False,0.0
42
- EBM,Tuned,Tree-based,0.006145874472476498,695.6092947224777,16.441161926872642,1265.4,False,0.0
43
- LightGBM,Default,Tree-based,0.04475798376061936,0.2873206973075867,14.819832856038637,1258.1,False,0.0
44
- RandomForest,Tuned + Ens.,Tree-based,0.34073913033041453,65.99906390887335,15.099597886629788,1244.0,False,0.0
45
- EBM,Default,Tree-based,0.005423073797379131,2.1751921610874043,17.869797396618495,1220.7,False,0.0
46
- ExtraTrees,Tuned + Ens.,Tree-based,0.1990111468528113,49.61278913069225,15.304205438801747,1216.6,False,0.0
47
- RandomForest,Tuned,Tree-based,0.024385081397162542,65.99906390887335,15.743878246297873,1214.3,False,0.0
48
- FastaiMLP,Tuned + Ens.,Neural Network,1.6581625314855013,351.94124054616117,17.191647439682967,1200.9,False,0.0
49
- SAP-RPT-OSS,Default,Foundation Model,10.416202336590205,11.438056903606038,16.93308052112088,1200.6,False,0.0
50
- iLTM,Default,Foundation Model,9.620405688285826,88.27017877459726,18.48774133469958,1179.4,False,0.0
51
- ExtraTrees,Tuned,Tree-based,0.01531327607338889,49.61278913069225,16.303086509449745,1173.8,False,0.0
52
- TorchMLP,Default,Neural Network,0.03884902778397399,2.1848199639320423,18.14527829243004,1126.1,False,0.0
53
- PerpetualBooster,Tuned + Ens.,Tree-based,0.5149235804875691,20.261904767465836,21.63305670520802,1109.8,False,0.0
54
- FastaiMLP,Tuned,Neural Network,0.11417552222870819,351.94124054616117,19.293775373383966,1101.1,False,0.0
55
- PerpetualBooster,Tuned,Tree-based,0.1995710637834337,20.261904767465836,22.7471582872999,1059.6,False,0.0
56
- xRFM,Default,Other,0.1448756257440684,2.4940806965258653,25.979630532218167,1046.2,False,0.0
57
- FastaiMLP,Default,Neural Network,0.11118662754860731,1.5145310856529652,22.86430904829306,1031.1,False,0.0
58
- RandomForest,Default,Tree-based,0.020899149004195042,0.19915272527270847,21.197350295416424,1000.0,False,0.0
59
- TabSTAR,Tuned,Foundation Model,1.7237891060437345,3381.771216219202,25.807196939028948,987.6,False,0.0
60
- TabSTAR,Tuned + Ens.,Foundation Model,5.282666632972537,3381.771216219202,25.780693599443993,986.6,False,0.0
61
- ExtraTrees,Default,Tree-based,0.07330450852711995,0.48425838390986126,22.789483920160308,976.4,False,0.0
62
- PerpetualBooster,Default,Tree-based,0.0149545775519477,1.498562971264291,29.423535438616728,900.7,False,0.0
63
- TabSTAR,Default,Foundation Model,1.9637818715369053,110.78162664148357,30.685363334130848,847.7,False,0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/pareto_n_configs_imp.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a06f1ddd5fb23e76494005fb7b89c5b14707991a6d372c5659393437790f6f4e
3
- size 329290
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/tuning-impact-elo.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:7185f3fddd76776304b7ce81d0589553ffcc5e7699704ffd92f3612034a0f893
3
- size 106865
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/tuning_trajectories.csv DELETED
@@ -1,163 +0,0 @@
1
- method,family,x_train,x_infer,imp,elo,n_configs,imputed,imputed_pct
2
- CatBoost,Tree-based,2.469413828723084,0.010282764438985344,9.108385412408444,1592.4,1.0,False,0.0
3
- CatBoost,Tree-based,4.4162445684880955,0.04068073453140345,9.115993897741033,1597.2,2.0,False,0.0
4
- CatBoost,Tree-based,12.876612222369168,0.06886368438989382,9.077949577458742,1614.6,5.0,False,0.0
5
- CatBoost,Tree-based,24.258974220991107,0.09260063686411787,9.058859454142612,1619.0,10.0,False,0.0
6
- CatBoost,Tree-based,62.05166363782045,0.14800972629415846,8.94676664463184,1630.9,25.0,False,0.0
7
- CatBoost,Tree-based,122.58001743340024,0.17889789170675716,8.907062350602835,1636.2,50.0,False,0.0
8
- CatBoost,Tree-based,242.64107636346372,0.21238325680356107,8.92346583669855,1635.5,100.0,False,0.0
9
- CatBoost,Tree-based,361.12835802610556,0.22857363602101013,8.93120303627158,1634.4,150.0,False,0.0
10
- CatBoost,Tree-based,489.92741010372185,0.25701532770653723,8.939773822979856,1632.8,201.0,False,0.0
11
- ChimeraBoost,Tree-based,0.47897883160115473,0.02253709445919907,13.189454574606707,1436.0,1.0,False,0.0
12
- ChimeraBoost,Tree-based,0.9589536975181837,0.04707955012195989,12.780468930510342,1466.1,2.0,False,0.0
13
- ChimeraBoost,Tree-based,2.3562175890436245,0.09651491905580373,12.474418307166582,1487.0,5.0,False,0.0
14
- ChimeraBoost,Tree-based,4.7221268742289215,0.14469563000700886,12.239180925343982,1504.5,10.0,False,0.0
15
- ChimeraBoost,Tree-based,11.925520358640576,0.20166683840835442,12.049733976311524,1519.9,25.0,False,0.0
16
- ChimeraBoost,Tree-based,23.876897602770455,0.24104859679665822,11.913101781047567,1529.1,50.0,False,0.0
17
- ChimeraBoost,Tree-based,47.75183320297697,0.2647783876343018,11.775566699064154,1538.0,100.0,False,0.0
18
- ChimeraBoost,Tree-based,71.58606156368943,0.28902400667036765,11.715015921705005,1542.4,150.0,False,0.0
19
- ChimeraBoost,Tree-based,95.91691558887598,0.30641840856334224,11.695769572500863,1545.6,201.0,False,0.0
20
- EBM,Tree-based,2.1751921610874043,0.005423073797379131,17.880342849641814,1226.6,1.0,False,0.0
21
- EBM,Tree-based,6.66369259252883,0.01166082767964927,17.194191521115066,1252.7,2.0,False,0.0
22
- EBM,Tree-based,17.233925805413904,0.022699023320909735,16.611687689639407,1279.2,5.0,False,0.0
23
- EBM,Tree-based,34.57775587659134,0.03331359493060592,16.356047022321825,1292.6,10.0,False,0.0
24
- EBM,Tree-based,87.92451656340509,0.049757168812449036,16.052139850599062,1310.8,25.0,False,0.0
25
- EBM,Tree-based,174.9652611713922,0.05930088518555479,15.899420591867939,1322.1,50.0,False,0.0
26
- EBM,Tree-based,345.06715788569375,0.0709126161428919,15.738671469168668,1333.5,100.0,False,0.0
27
- EBM,Tree-based,520.7521321905026,0.0763826625184994,15.682540075474053,1338.6,150.0,False,0.0
28
- EBM,Tree-based,695.6092947224777,0.08519206070345582,15.601358072339396,1345.5,201.0,False,0.0
29
- ExtraTrees,Tree-based,0.48425838390986126,0.07330450852711995,22.79988950966345,973.5,1.0,False,0.0
30
- ExtraTrees,Tree-based,0.8466137520036096,0.07522212407038217,20.659729306554848,1029.8,2.0,False,0.0
31
- ExtraTrees,Tree-based,1.692317521137873,0.0926460135839083,17.462172250613623,1121.5,5.0,False,0.0
32
- ExtraTrees,Tree-based,2.929179805844217,0.10109344429242911,16.311064736513742,1167.0,10.0,False,0.0
33
- ExtraTrees,Tree-based,6.61473647238103,0.10828184879460544,15.560182328594163,1201.4,25.0,False,0.0
34
- ExtraTrees,Tree-based,12.710816754735825,0.14385828902585665,15.392998219548856,1213.9,50.0,False,0.0
35
- ExtraTrees,Tree-based,24.941682372165904,0.16087820180002574,15.26699302280187,1223.4,100.0,False,0.0
36
- ExtraTrees,Tree-based,37.13843182260326,0.18771563305900177,15.265549059518888,1226.1,150.0,False,0.0
37
- ExtraTrees,Tree-based,49.61278913069225,0.1990111468528113,15.316095966675125,1228.5,201.0,False,0.0
38
- FastaiMLP,Neural Network,1.5145310856529652,0.11118662754860731,22.873841530144425,1039.3,1.0,False,0.0
39
- FastaiMLP,Neural Network,3.1588915156956174,0.22199569005113567,21.853790659634655,1065.8,2.0,False,0.0
40
- FastaiMLP,Neural Network,8.59563123829451,0.4051013545327364,20.52389504257173,1113.9,5.0,False,0.0
41
- FastaiMLP,Neural Network,17.78186248206276,0.6441505685161579,19.750939156228988,1138.1,10.0,False,0.0
42
- FastaiMLP,Neural Network,42.53008857176902,0.9551913220836626,18.79520904183076,1166.3,25.0,False,0.0
43
- FastaiMLP,Neural Network,85.67628226317562,1.2793194595040644,18.216855293020117,1183.4,50.0,False,0.0
44
- FastaiMLP,Neural Network,173.7598987199963,1.5591641511917327,17.729624605224796,1197.3,100.0,False,0.0
45
- FastaiMLP,Neural Network,261.7851807624679,1.6900218906632645,17.401845831784602,1210.6,150.0,False,0.0
46
- FastaiMLP,Neural Network,351.94124054616117,1.658906059585989,17.197757082763808,1219.8,201.0,False,0.0
47
- LightGBM,Tree-based,0.2873206973075867,0.04475798376061936,14.831406830988078,1265.3,1.0,False,0.0
48
- LightGBM,Tree-based,0.931650224659178,0.423415924843989,14.227285012543916,1316.7,2.0,False,0.0
49
- LightGBM,Tree-based,2.728670285380442,0.7433885464019945,13.077108942307165,1416.7,5.0,False,0.0
50
- LightGBM,Tree-based,6.1477936875711485,0.9410923145319288,12.216587804790636,1486.0,10.0,False,0.0
51
- LightGBM,Tree-based,16.52097248547071,1.3202136892725407,11.206256977787026,1552.1,25.0,False,0.0
52
- LightGBM,Tree-based,32.67977659086273,1.4195738396100834,10.77603904990665,1588.5,50.0,False,0.0
53
- LightGBM,Tree-based,65.3538276872704,2.0760086735335515,10.583483894061576,1603.7,100.0,False,0.0
54
- LightGBM,Tree-based,98.42128335857386,2.2773590031797486,10.522416903623661,1608.7,150.0,False,0.0
55
- LightGBM,Tree-based,131.56200707040514,2.638673830958025,10.484769236100053,1615.4,201.0,False,0.0
56
- LimiX,Foundation Model,6.553292592842144,5.408573077515913,16.14157585284815,1298.5,1.0,False,0.0
57
- ModernNCA,Neural Network,8.063770877226162,0.24960134666003478,14.341711205023394,1311.4,1.0,False,0.0
58
- ModernNCA,Neural Network,21.753839521403215,0.5785744608262396,12.878270477194889,1383.0,2.0,False,0.0
59
- ModernNCA,Neural Network,60.91776288315985,1.29474806549814,11.027959170982662,1499.2,5.0,False,0.0
60
- ModernNCA,Neural Network,121.85928580479494,2.1427076773113676,10.344152870747507,1546.2,10.0,False,0.0
61
- ModernNCA,Neural Network,292.3137710755714,2.7736882795804276,9.611889968072926,1599.2,25.0,False,0.0
62
- ModernNCA,Neural Network,629.890040030536,3.9630762476559322,9.307735484335712,1621.1,50.0,False,0.0
63
- ModernNCA,Neural Network,1243.5591974675485,5.167921375609095,9.181770303214156,1633.7,100.0,False,0.0
64
- ModernNCA,Neural Network,1862.5519447854,5.876817380350334,9.122250703177043,1637.8,150.0,False,0.0
65
- ModernNCA,Neural Network,2526.306680600179,6.104160729998767,9.09173861372904,1640.3,201.0,False,0.0
66
- PerpetualBooster,Tree-based,1.498562971264291,0.0149545775519477,29.689070512344767,914.4,1.0,False,0.0
67
- PerpetualBooster,Tree-based,6.4317773956676225,0.14601458387603072,27.150126117960287,995.4,2.0,False,0.0
68
- PerpetualBooster,Tree-based,16.949305660221782,0.444796272511348,21.76117480747134,1110.0,5.0,False,0.0
69
- PerpetualBooster,Tree-based,20.261904767465836,0.5149235804875691,21.637241667958527,1119.5,6.0,False,0.0
70
- RandomForest,Tree-based,0.19915272527270847,0.020899149004195042,21.213144829853707,999.5,1.0,False,0.0
71
- RandomForest,Tree-based,0.5021873110599798,0.045901973979854016,17.69477084542158,1106.7,2.0,False,0.0
72
- RandomForest,Tree-based,1.489455051236279,0.11282485912166518,16.252525288491515,1176.5,5.0,False,0.0
73
- RandomForest,Tree-based,3.130777319214283,0.13673928726731627,15.703690077118287,1208.8,10.0,False,0.0
74
- RandomForest,Tree-based,8.02025679639823,0.20254017138230673,15.340406965354184,1234.5,25.0,False,0.0
75
- RandomForest,Tree-based,16.237787158411987,0.2523558649222056,15.214174130159272,1245.0,50.0,False,0.0
76
- RandomForest,Tree-based,32.671093442836856,0.2972209594249725,15.126789502908613,1254.8,100.0,False,0.0
77
- RandomForest,Tree-based,49.171965802832155,0.33106692790985104,15.085348041401572,1259.4,150.0,False,0.0
78
- RandomForest,Tree-based,65.99906390887335,0.34148336141365077,15.111140652140218,1262.7,201.0,False,0.0
79
- RealMLP,Neural Network,6.7532462416847245,0.23498184801373212,15.100567151344697,1281.1,1.0,False,0.0
80
- RealMLP,Neural Network,15.527578690794181,0.3080259999395399,12.907226004650202,1413.1,2.0,False,0.0
81
- RealMLP,Neural Network,39.124153040818086,0.5106934438586126,11.363558126850805,1512.9,5.0,False,0.0
82
- RealMLP,Neural Network,82.74224081710975,0.759436157944363,10.5674293142504,1553.2,10.0,False,0.0
83
- RealMLP,Neural Network,213.27613858935567,0.9843404045345187,9.848178322594768,1599.1,25.0,False,0.0
84
- RealMLP,Neural Network,427.90915376015505,1.1860326968189097,9.529736811839793,1614.8,50.0,False,0.0
85
- RealMLP,Neural Network,848.9386425085199,1.4580786339418361,9.160122719593092,1634.3,100.0,False,0.0
86
- RealMLP,Neural Network,1278.8937139162222,1.5889939536507325,9.003642529752812,1645.3,150.0,False,0.0
87
- RealMLP,Neural Network,1719.820290101634,1.6682080327694422,8.92878682894443,1654.4,201.0,False,0.0
88
- RealTabPFN-2.5,Foundation Model,1.8760518371058228,0.644969598113518,10.091872578679544,1542.8,1.0,False,0.0
89
- RealTabPFN-2.5,Foundation Model,5.127864715472546,1.4937099645347298,10.018663162993988,1552.4,2.0,False,0.0
90
- RealTabPFN-2.5,Foundation Model,16.206992788783356,3.2513268046584503,9.848206787771563,1566.8,5.0,False,0.0
91
- RealTabPFN-2.5,Foundation Model,34.25360422679489,4.991835645809406,9.701576186330296,1583.0,10.0,False,0.0
92
- RealTabPFN-2.5,Foundation Model,88.96282194243713,6.462092531240927,9.358425943462754,1623.9,25.0,False,0.0
93
- RealTabPFN-2.5,Foundation Model,181.39522434540766,9.411482770309476,9.00034093600866,1645.7,50.0,False,0.0
94
- RealTabPFN-2.5,Foundation Model,363.91723618262904,11.623618059298572,8.777908024057808,1656.9,100.0,False,0.0
95
- RealTabPFN-2.5,Foundation Model,547.7650992279482,11.995516969136737,8.657656126655123,1660.4,150.0,False,0.0
96
- RealTabPFN-2.5,Foundation Model,735.5848736648751,11.735986151605083,8.585639428162168,1660.9,201.0,False,0.0
97
- SAP-RPT-OSS,Foundation Model,11.438056903606038,10.416202336590205,16.941314181065422,1215.7,1.0,False,0.0
98
- TabDPT,Foundation Model,46.6209023206833,43.737812222627376,15.320041044124682,1343.2,1.0,False,0.0
99
- TabDPT,Foundation Model,70.03488466291391,60.76345722760032,14.195649530423754,1371.8,2.0,False,0.0
100
- TabDPT,Foundation Model,129.29601127069574,97.7966669956843,13.094383264418624,1413.9,5.0,False,0.0
101
- TabDPT,Foundation Model,230.72939494613806,126.13170818355633,12.678445777759691,1431.4,10.0,False,0.0
102
- TabDPT,Foundation Model,589.0616829285759,211.8920837189545,11.724325816257148,1477.2,25.0,False,0.0
103
- TabDPT,Foundation Model,1187.757830840611,283.78344604791266,11.191253011736734,1502.7,50.0,False,0.0
104
- TabDPT,Foundation Model,2376.2279314343073,356.1524517375418,10.813507343563613,1519.8,100.0,False,0.0
105
- TabDPT,Foundation Model,3574.191913268326,402.5853130690112,10.483151139306383,1533.6,150.0,False,0.0
106
- TabDPT,Foundation Model,4786.598933193933,433.01544407663886,10.296860814024953,1538.9,201.0,False,0.0
107
- TabDPT-Turbo,Foundation Model,0.7535798581633265,0.1795394574862431,10.819902208137352,1540.5,1.0,False,0.0
108
- TabFM,Foundation Model,27.288414010629126,13.118891688893655,2.6842884758658836,1990.2,1.0,False,0.0
109
- TabICLv2,Foundation Model,0.6015671441553114,0.118628191947937,6.865956109703099,1713.3,1.0,False,0.0
110
- TabM,Neural Network,2.1306936635677576,0.047061096558805166,13.99396716730505,1336.4,1.0,False,0.0
111
- TabM,Neural Network,6.720132302065123,0.10166420156235179,13.138602472702448,1384.2,2.0,False,0.0
112
- TabM,Neural Network,26.071931655972485,0.19658337075272886,12.076710839994865,1440.9,5.0,False,0.0
113
- TabM,Neural Network,56.6403905325952,0.2690374607115522,11.465419545965009,1482.1,10.0,False,0.0
114
- TabM,Neural Network,151.63250845437173,0.34533908128738405,10.915087566605157,1514.9,25.0,False,0.0
115
- TabM,Neural Network,305.405015900313,0.46416595419440415,10.634788115506625,1536.6,50.0,False,0.0
116
- TabM,Neural Network,598.1660502680137,0.5371776271588682,10.40920433362231,1551.8,100.0,False,0.0
117
- TabM,Neural Network,909.6246432442554,0.5567799536795643,10.320563967083489,1559.9,150.0,False,0.0
118
- TabM,Neural Network,1236.8778796591432,0.5682121276236716,10.262306941439089,1568.2,201.0,False,0.0
119
- TabPFN-2.6,Foundation Model,2.7558592936727737,0.700875014724915,7.860528976942049,1746.6,1.0,False,0.0
120
- TabPFN-3,Foundation Model,1.0099025842179519,0.21472657924676145,5.883486403593643,1798.7,1.0,False,0.0
121
- TabSTAR,Foundation Model,110.78162664148357,1.9637818715369053,30.69176142205053,842.3,1.0,False,0.0
122
- TabSTAR,Foundation Model,125.57112346948114,1.933477852272125,30.6098027549634,858.0,2.0,False,0.0
123
- TabSTAR,Foundation Model,166.49912766533103,2.140647516193624,30.457152233542967,864.2,5.0,False,0.0
124
- TabSTAR,Foundation Model,216.82609728692088,2.4886555762895703,30.017913515556792,881.7,10.0,False,0.0
125
- TabSTAR,Foundation Model,435.6268489691195,2.9679019309527157,29.053271930943918,906.8,25.0,False,0.0
126
- TabSTAR,Foundation Model,841.7488888840559,3.4310027254645297,27.865416610529476,933.8,50.0,False,0.0
127
- TabSTAR,Foundation Model,1663.686576013856,4.443681021054585,27.08082788380544,956.7,100.0,False,0.0
128
- TabSTAR,Foundation Model,2487.204912475645,4.943553034388052,26.384678054770276,981.0,150.0,False,0.0
129
- TabSTAR,Foundation Model,3381.771216219202,5.282666632972537,25.788716656340082,1007.2,201.0,False,0.0
130
- TabSwift,Foundation Model,0.5069992698737326,0.12716708249348388,13.350681903466016,1362.2,1.0,False,0.0
131
- TorchMLP,Neural Network,2.1848199639320423,0.03884902778397399,18.153580763501207,1135.4,1.0,False,0.0
132
- TorchMLP,Neural Network,9.222603334986646,0.09146565885667123,16.913855088278495,1182.8,2.0,False,0.0
133
- TorchMLP,Neural Network,41.23639970605849,0.2165503089261113,15.542102939673713,1240.3,5.0,False,0.0
134
- TorchMLP,Neural Network,91.32376520040506,0.35785402203318467,14.726269057806753,1284.5,10.0,False,0.0
135
- TorchMLP,Neural Network,218.54732020220172,0.5218944892827895,13.843543448675543,1325.1,25.0,False,0.0
136
- TorchMLP,Neural Network,447.05075486815855,0.7429074477444512,13.331475602418127,1355.0,50.0,False,0.0
137
- TorchMLP,Neural Network,866.7256429369004,0.9478582393719392,12.928400950565655,1377.2,100.0,False,0.0
138
- TorchMLP,Neural Network,1331.6601195386634,1.0346365316168704,12.685478783982992,1390.2,150.0,False,0.0
139
- TorchMLP,Neural Network,1770.5620854905956,1.0691781291613058,12.462997864617684,1401.4,201.0,False,0.0
140
- XGBoost,Tree-based,0.4869354633908524,0.053416368885586527,12.920703471704298,1387.7,1.0,False,0.0
141
- XGBoost,Tree-based,2.4341782987194773,0.17931842124252992,11.917596563649761,1466.8,2.0,False,0.0
142
- XGBoost,Tree-based,7.3419221790132845,0.2826887094137962,11.365912227698736,1515.0,5.0,False,0.0
143
- XGBoost,Tree-based,13.425391893256847,0.29683045128481406,11.001231426277393,1536.9,10.0,False,0.0
144
- XGBoost,Tree-based,35.1370362217863,0.40283655412991837,10.749441963853915,1552.3,25.0,False,0.0
145
- XGBoost,Tree-based,69.83880191326698,0.4362798233826955,10.644994504895712,1560.2,50.0,False,0.0
146
- XGBoost,Tree-based,138.68430728332427,0.4803960764408111,10.57795589179427,1567.5,100.0,False,0.0
147
- XGBoost,Tree-based,209.60031705983081,0.5237722573280333,10.566097258404868,1571.0,150.0,False,0.0
148
- XGBoost,Tree-based,282.1320041804491,0.5608426218032837,10.56061766714314,1574.8,201.0,False,0.0
149
- iLTM,Foundation Model,88.27017877459726,9.620405688285826,18.49837810390806,1192.9,1.0,False,0.0
150
- iLTM,Foundation Model,168.23671986991735,18.955818040688833,16.389045401402022,1247.3,2.0,False,0.0
151
- iLTM,Foundation Model,410.88335194101177,33.21024323942741,16.218624325209394,1345.3,5.0,False,0.0
152
- iLTM,Foundation Model,805.521975186799,46.92423318461996,13.559497444839424,1442.5,10.0,False,0.0
153
- iLTM,Foundation Model,1992.2815124103738,66.5740410026117,10.270428232288518,1578.8,25.0,False,0.0
154
- iLTM,Foundation Model,2067.952899612129,65.19515312136267,10.233826926607986,1584.4,26.0,False,0.0
155
- xRFM,Other,2.4940806965258653,0.1448756257440684,25.98755778666362,1051.9,1.0,False,0.0
156
- xRFM,Other,5.313335044139122,0.19036624122472157,21.3468517178997,1127.7,2.0,False,0.0
157
- xRFM,Other,13.802098162260378,0.2632687113094409,16.135396193164826,1255.0,5.0,False,0.0
158
- xRFM,Other,28.327072009638513,0.38188561104171487,13.900279138547264,1372.9,10.0,False,0.0
159
- xRFM,Other,70.60474405079418,0.5413359982156494,12.97431812352308,1424.6,25.0,False,0.0
160
- xRFM,Other,140.54533237320845,0.6886364122629532,12.624894430651533,1441.4,50.0,False,0.0
161
- xRFM,Other,281.12441629309126,0.8346908134535932,12.489108203595404,1446.3,100.0,False,0.0
162
- xRFM,Other,422.35324605180153,0.899204071689565,12.448018897829023,1447.6,150.0,False,0.0
163
- xRFM,Other,566.736982568635,0.9679699004975301,12.462818714897264,1446.1,201.0,False,0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/tuning_trajectories_explorer.html DELETED
@@ -1,1230 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena tuning trajectories</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Two-column layout: controls + chips in a side panel, chart beside it.
269
- ``chips-right`` mirrors the columns. Wraps to stacked when narrow. */
270
- .explorer-grid { display: flex; gap: 18px; align-items: flex-start; }
271
- .explorer-grid.chips-right { flex-direction: row-reverse; }
272
- .sidebox { flex: 0 0 330px; min-width: 250px; display: flex; flex-direction: column; gap: 10px; }
273
- .mainbox { flex: 1 1 auto; min-width: 0; }
274
- @media (max-width: 860px) {
275
- .explorer-grid { flex-wrap: wrap; }
276
- .sidebox { flex: 1 1 100%; }
277
- }
278
-
279
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
280
- .chartbox { position: relative; }
281
- /* The chart is sized in device pixels by render() rather than scaled from a
282
- viewBox: scaling stretched the type along with the plot, and a viewBox tall
283
- enough to read made the whole panel own the screen. */
284
- .chartbox svg { display: block; }
285
- /* Chips scroll within the column so the panel height follows the chart. */
286
- .sidebox .chips { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent; }
287
- .sidebox .chips::-webkit-scrollbar { width: 9px; }
288
- .sidebox .chips::-webkit-scrollbar-thumb {
289
- background: var(--pt-muted); border-radius: 8px; border: 3px solid transparent; background-clip: content-box;
290
- }
291
- </style>
292
- </head>
293
- <body>
294
- <div class="viewbar">
295
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
296
- </div>
297
- <p class="explorer-title" id="title"></p>
298
- <div class="explorer-grid" id="grid">
299
- <div class="sidebox">
300
- <div class="controls">
301
- <label class="metricpick" id="metricpick" hidden>Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <div class="btnrow">
305
- <button class="btn" id="btn-front">Pareto front</button>
306
- <button class="btn" id="btn-all">All</button>
307
- <button class="btn" id="btn-none">Clear</button>
308
- </div>
309
- <span class="hint">Click methods or family buttons to highlight &middot; hover points for details</span>
310
- </div>
311
- <div class="chips" id="chips"></div>
312
- </div>
313
- <div class="mainbox">
314
- <div class="exportbar" id="exportbar" hidden>
315
- <span class="hint">Export figure</span>
316
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
317
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
318
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
319
- </div>
320
- <!-- Legend above the chart so readers decode the marks before the data. -->
321
- <div class="legendstrip" id="legendstrip"></div>
322
- <div class="chartbox" id="chartbox">
323
- <svg id="chart" role="img" aria-label="Pareto front explorer"></svg>
324
- <div class="tooltip"></div>
325
- </div>
326
- </div>
327
- </div>
328
- <details class="datatable">
329
- <summary>Data table</summary>
330
- <div class="tblwrap" id="tblwrap"></div>
331
- </details>
332
-
333
- <script>
334
- (function () {
335
- "use strict";
336
- const CONFIG = {"mode": "trajectory", "title": null, "metrics": [{"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": ""}], "xAxes": [{"key": "x_train", "label": "Train time", "axisLabel": "Train time per 1K samples (s), median \u2014 log scale", "short": "Train (s/1K, median)"}], "chipsSide": "right"};
337
- const POINTS = [{"method":"CatBoost","family":"Tree-based","x_train":2.4694138287,"imp":9.1083854124,"elo":1592.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"CatBoost","family":"Tree-based","x_train":4.4162445685,"imp":9.1159938977,"elo":1597.2,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"CatBoost","family":"Tree-based","x_train":12.8766122224,"imp":9.0779495775,"elo":1614.6,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"CatBoost","family":"Tree-based","x_train":24.258974221,"imp":9.0588594541,"elo":1619.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"CatBoost","family":"Tree-based","x_train":62.0516636378,"imp":8.9467666446,"elo":1630.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"CatBoost","family":"Tree-based","x_train":122.5800174334,"imp":8.9070623506,"elo":1636.2,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"CatBoost","family":"Tree-based","x_train":242.6410763635,"imp":8.9234658367,"elo":1635.5,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"CatBoost","family":"Tree-based","x_train":361.1283580261,"imp":8.9312030363,"elo":1634.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"CatBoost","family":"Tree-based","x_train":489.9274101037,"imp":8.939773823,"elo":1632.8,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":0.4789788316,"imp":13.1894545746,"elo":1436.0,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":0.9589536975,"imp":12.7804689305,"elo":1466.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":2.356217589,"imp":12.4744183072,"elo":1487.0,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":4.7221268742,"imp":12.2391809253,"elo":1504.5,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":11.9255203586,"imp":12.0497339763,"elo":1519.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":23.8768976028,"imp":11.913101781,"elo":1529.1,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":47.751833203,"imp":11.7755666991,"elo":1538.0,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":71.5860615637,"imp":11.7150159217,"elo":1542.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ChimeraBoost","family":"Tree-based","x_train":95.9169155889,"imp":11.6957695725,"elo":1545.6,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"EBM","family":"Tree-based","x_train":2.1751921611,"imp":17.8803428496,"elo":1226.6,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"EBM","family":"Tree-based","x_train":6.6636925925,"imp":17.1941915211,"elo":1252.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"EBM","family":"Tree-based","x_train":17.2339258054,"imp":16.6116876896,"elo":1279.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"EBM","family":"Tree-based","x_train":34.5777558766,"imp":16.3560470223,"elo":1292.6,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"EBM","family":"Tree-based","x_train":87.9245165634,"imp":16.0521398506,"elo":1310.8,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"EBM","family":"Tree-based","x_train":174.9652611714,"imp":15.8994205919,"elo":1322.1,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"EBM","family":"Tree-based","x_train":345.0671578857,"imp":15.7386714692,"elo":1333.5,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"EBM","family":"Tree-based","x_train":520.7521321905,"imp":15.6825400755,"elo":1338.6,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"EBM","family":"Tree-based","x_train":695.6092947225,"imp":15.6013580723,"elo":1345.5,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"ExtraTrees","family":"Tree-based","x_train":0.4842583839,"imp":22.7998895097,"elo":973.5,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ExtraTrees","family":"Tree-based","x_train":0.846613752,"imp":20.6597293066,"elo":1029.8,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ExtraTrees","family":"Tree-based","x_train":1.6923175211,"imp":17.4621722506,"elo":1121.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ExtraTrees","family":"Tree-based","x_train":2.9291798058,"imp":16.3110647365,"elo":1167.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ExtraTrees","family":"Tree-based","x_train":6.6147364724,"imp":15.5601823286,"elo":1201.4,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ExtraTrees","family":"Tree-based","x_train":12.7108167547,"imp":15.3929982195,"elo":1213.9,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ExtraTrees","family":"Tree-based","x_train":24.9416823722,"imp":15.2669930228,"elo":1223.4,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ExtraTrees","family":"Tree-based","x_train":37.1384318226,"imp":15.2655490595,"elo":1226.1,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ExtraTrees","family":"Tree-based","x_train":49.6127891307,"imp":15.3160959667,"elo":1228.5,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"FastaiMLP","family":"Neural Network","x_train":1.5145310857,"imp":22.8738415301,"elo":1039.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"FastaiMLP","family":"Neural Network","x_train":3.1588915157,"imp":21.8537906596,"elo":1065.8,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"FastaiMLP","family":"Neural Network","x_train":8.5956312383,"imp":20.5238950426,"elo":1113.9,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"FastaiMLP","family":"Neural Network","x_train":17.7818624821,"imp":19.7509391562,"elo":1138.1,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"FastaiMLP","family":"Neural Network","x_train":42.5300885718,"imp":18.7952090418,"elo":1166.3,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"FastaiMLP","family":"Neural Network","x_train":85.6762822632,"imp":18.216855293,"elo":1183.4,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"FastaiMLP","family":"Neural Network","x_train":173.75989872,"imp":17.7296246052,"elo":1197.3,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"FastaiMLP","family":"Neural Network","x_train":261.7851807625,"imp":17.4018458318,"elo":1210.6,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"FastaiMLP","family":"Neural Network","x_train":351.9412405462,"imp":17.1977570828,"elo":1219.8,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"LightGBM","family":"Tree-based","x_train":0.2873206973,"imp":14.831406831,"elo":1265.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"LightGBM","family":"Tree-based","x_train":0.9316502247,"imp":14.2272850125,"elo":1316.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"LightGBM","family":"Tree-based","x_train":2.7286702854,"imp":13.0771089423,"elo":1416.7,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"LightGBM","family":"Tree-based","x_train":6.1477936876,"imp":12.2165878048,"elo":1486.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"LightGBM","family":"Tree-based","x_train":16.5209724855,"imp":11.2062569778,"elo":1552.1,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"LightGBM","family":"Tree-based","x_train":32.6797765909,"imp":10.7760390499,"elo":1588.5,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"LightGBM","family":"Tree-based","x_train":65.3538276873,"imp":10.5834838941,"elo":1603.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"LightGBM","family":"Tree-based","x_train":98.4212833586,"imp":10.5224169036,"elo":1608.7,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"LightGBM","family":"Tree-based","x_train":131.5620070704,"imp":10.4847692361,"elo":1615.4,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"LimiX","family":"Foundation Model","x_train":6.5532925928,"imp":16.1415758528,"elo":1298.5,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ModernNCA","family":"Neural Network","x_train":8.0637708772,"imp":14.341711205,"elo":1311.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"ModernNCA","family":"Neural Network","x_train":21.7538395214,"imp":12.8782704772,"elo":1383.0,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"ModernNCA","family":"Neural Network","x_train":60.9177628832,"imp":11.027959171,"elo":1499.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"ModernNCA","family":"Neural Network","x_train":121.8592858048,"imp":10.3441528707,"elo":1546.2,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"ModernNCA","family":"Neural Network","x_train":292.3137710756,"imp":9.6118899681,"elo":1599.2,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"ModernNCA","family":"Neural Network","x_train":629.8900400305,"imp":9.3077354843,"elo":1621.1,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"ModernNCA","family":"Neural Network","x_train":1243.5591974675,"imp":9.1817703032,"elo":1633.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"ModernNCA","family":"Neural Network","x_train":1862.5519447854,"imp":9.1222507032,"elo":1637.8,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"ModernNCA","family":"Neural Network","x_train":2526.3066806002,"imp":9.0917386137,"elo":1640.3,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":1.4985629713,"imp":29.6890705123,"elo":914.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":6.4317773957,"imp":27.150126118,"elo":995.4,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":16.9493056602,"imp":21.7611748075,"elo":1110.0,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"PerpetualBooster","family":"Tree-based","x_train":20.2619047675,"imp":21.637241668,"elo":1119.5,"imputed":false,"imputed_pct":0.0,"n_configs":6.0},{"method":"RandomForest","family":"Tree-based","x_train":0.1991527253,"imp":21.2131448299,"elo":999.5,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RandomForest","family":"Tree-based","x_train":0.5021873111,"imp":17.6947708454,"elo":1106.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RandomForest","family":"Tree-based","x_train":1.4894550512,"imp":16.2525252885,"elo":1176.5,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RandomForest","family":"Tree-based","x_train":3.1307773192,"imp":15.7036900771,"elo":1208.8,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RandomForest","family":"Tree-based","x_train":8.0202567964,"imp":15.3404069654,"elo":1234.5,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RandomForest","family":"Tree-based","x_train":16.2377871584,"imp":15.2141741302,"elo":1245.0,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RandomForest","family":"Tree-based","x_train":32.6710934428,"imp":15.1267895029,"elo":1254.8,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RandomForest","family":"Tree-based","x_train":49.1719658028,"imp":15.0853480414,"elo":1259.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RandomForest","family":"Tree-based","x_train":65.9990639089,"imp":15.1111406521,"elo":1262.7,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"RealMLP","family":"Neural Network","x_train":6.7532462417,"imp":15.1005671513,"elo":1281.1,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RealMLP","family":"Neural Network","x_train":15.5275786908,"imp":12.9072260047,"elo":1413.1,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RealMLP","family":"Neural Network","x_train":39.1241530408,"imp":11.3635581269,"elo":1512.9,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RealMLP","family":"Neural Network","x_train":82.7422408171,"imp":10.5674293143,"elo":1553.2,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RealMLP","family":"Neural Network","x_train":213.2761385894,"imp":9.8481783226,"elo":1599.1,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RealMLP","family":"Neural Network","x_train":427.9091537602,"imp":9.5297368118,"elo":1614.8,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RealMLP","family":"Neural Network","x_train":848.9386425085,"imp":9.1601227196,"elo":1634.3,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RealMLP","family":"Neural Network","x_train":1278.8937139162,"imp":9.0036425298,"elo":1645.3,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RealMLP","family":"Neural Network","x_train":1719.8202901016,"imp":8.9287868289,"elo":1654.4,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":1.8760518371,"imp":10.0918725787,"elo":1542.8,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":5.1278647155,"imp":10.018663163,"elo":1552.4,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":16.2069927888,"imp":9.8482067878,"elo":1566.8,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":34.2536042268,"imp":9.7015761863,"elo":1583.0,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":88.9628219424,"imp":9.3584259435,"elo":1623.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":181.3952243454,"imp":9.000340936,"elo":1645.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":363.9172361826,"imp":8.7779080241,"elo":1656.9,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":547.7650992279,"imp":8.6576561267,"elo":1660.4,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_train":735.5848736649,"imp":8.5856394282,"elo":1660.9,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"SAP-RPT-OSS","family":"Foundation Model","x_train":11.4380569036,"imp":16.9413141811,"elo":1215.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabDPT","family":"Foundation Model","x_train":46.6209023207,"imp":15.3200410441,"elo":1343.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabDPT","family":"Foundation Model","x_train":70.0348846629,"imp":14.1956495304,"elo":1371.8,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabDPT","family":"Foundation Model","x_train":129.2960112707,"imp":13.0943832644,"elo":1413.9,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabDPT","family":"Foundation Model","x_train":230.7293949461,"imp":12.6784457778,"elo":1431.4,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabDPT","family":"Foundation Model","x_train":589.0616829286,"imp":11.7243258163,"elo":1477.2,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabDPT","family":"Foundation Model","x_train":1187.7578308406,"imp":11.1912530117,"elo":1502.7,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabDPT","family":"Foundation Model","x_train":2376.2279314343,"imp":10.8135073436,"elo":1519.8,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabDPT","family":"Foundation Model","x_train":3574.1919132683,"imp":10.4831511393,"elo":1533.6,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabDPT","family":"Foundation Model","x_train":4786.5989331939,"imp":10.296860814,"elo":1538.9,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabDPT-Turbo","family":"Foundation Model","x_train":0.7535798582,"imp":10.8199022081,"elo":1540.5,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabFM","family":"Foundation Model","x_train":27.2884140106,"imp":2.6842884759,"elo":1990.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabICLv2","family":"Foundation Model","x_train":0.6015671442,"imp":6.8659561097,"elo":1713.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabM","family":"Neural Network","x_train":2.1306936636,"imp":13.9939671673,"elo":1336.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabM","family":"Neural Network","x_train":6.7201323021,"imp":13.1386024727,"elo":1384.2,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabM","family":"Neural Network","x_train":26.071931656,"imp":12.07671084,"elo":1440.9,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabM","family":"Neural Network","x_train":56.6403905326,"imp":11.465419546,"elo":1482.1,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabM","family":"Neural Network","x_train":151.6325084544,"imp":10.9150875666,"elo":1514.9,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabM","family":"Neural Network","x_train":305.4050159003,"imp":10.6347881155,"elo":1536.6,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabM","family":"Neural Network","x_train":598.166050268,"imp":10.4092043336,"elo":1551.8,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabM","family":"Neural Network","x_train":909.6246432443,"imp":10.3205639671,"elo":1559.9,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabM","family":"Neural Network","x_train":1236.8778796591,"imp":10.2623069414,"elo":1568.2,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabPFN-2.6","family":"Foundation Model","x_train":2.7558592937,"imp":7.8605289769,"elo":1746.6,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabPFN-3","family":"Foundation Model","x_train":1.0099025842,"imp":5.8834864036,"elo":1798.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabSTAR","family":"Foundation Model","x_train":110.7816266415,"imp":30.6917614221,"elo":842.3,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TabSTAR","family":"Foundation Model","x_train":125.5711234695,"imp":30.609802755,"elo":858.0,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TabSTAR","family":"Foundation Model","x_train":166.4991276653,"imp":30.4571522335,"elo":864.2,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TabSTAR","family":"Foundation Model","x_train":216.8260972869,"imp":30.0179135156,"elo":881.7,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TabSTAR","family":"Foundation Model","x_train":435.6268489691,"imp":29.0532719309,"elo":906.8,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TabSTAR","family":"Foundation Model","x_train":841.7488888841,"imp":27.8654166105,"elo":933.8,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TabSTAR","family":"Foundation Model","x_train":1663.6865760139,"imp":27.0808278838,"elo":956.7,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TabSTAR","family":"Foundation Model","x_train":2487.2049124756,"imp":26.3846780548,"elo":981.0,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TabSTAR","family":"Foundation Model","x_train":3381.7712162192,"imp":25.7887166563,"elo":1007.2,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"TabSwift","family":"Foundation Model","x_train":0.5069992699,"imp":13.3506819035,"elo":1362.2,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TorchMLP","family":"Neural Network","x_train":2.1848199639,"imp":18.1535807635,"elo":1135.4,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"TorchMLP","family":"Neural Network","x_train":9.222603335,"imp":16.9138550883,"elo":1182.8,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"TorchMLP","family":"Neural Network","x_train":41.2363997061,"imp":15.5421029397,"elo":1240.3,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"TorchMLP","family":"Neural Network","x_train":91.3237652004,"imp":14.7262690578,"elo":1284.5,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"TorchMLP","family":"Neural Network","x_train":218.5473202022,"imp":13.8435434487,"elo":1325.1,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"TorchMLP","family":"Neural Network","x_train":447.0507548682,"imp":13.3314756024,"elo":1355.0,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"TorchMLP","family":"Neural Network","x_train":866.7256429369,"imp":12.9284009506,"elo":1377.2,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"TorchMLP","family":"Neural Network","x_train":1331.6601195387,"imp":12.685478784,"elo":1390.2,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"TorchMLP","family":"Neural Network","x_train":1770.5620854906,"imp":12.4629978646,"elo":1401.4,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"XGBoost","family":"Tree-based","x_train":0.4869354634,"imp":12.9207034717,"elo":1387.7,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"XGBoost","family":"Tree-based","x_train":2.4341782987,"imp":11.9175965636,"elo":1466.8,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"XGBoost","family":"Tree-based","x_train":7.341922179,"imp":11.3659122277,"elo":1515.0,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"XGBoost","family":"Tree-based","x_train":13.4253918933,"imp":11.0012314263,"elo":1536.9,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"XGBoost","family":"Tree-based","x_train":35.1370362218,"imp":10.7494419639,"elo":1552.3,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"XGBoost","family":"Tree-based","x_train":69.8388019133,"imp":10.6449945049,"elo":1560.2,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"XGBoost","family":"Tree-based","x_train":138.6843072833,"imp":10.5779558918,"elo":1567.5,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"XGBoost","family":"Tree-based","x_train":209.6003170598,"imp":10.5660972584,"elo":1571.0,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"XGBoost","family":"Tree-based","x_train":282.1320041804,"imp":10.5606176671,"elo":1574.8,"imputed":false,"imputed_pct":0.0,"n_configs":201.0},{"method":"iLTM","family":"Foundation Model","x_train":88.2701787746,"imp":18.4983781039,"elo":1192.9,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"iLTM","family":"Foundation Model","x_train":168.2367198699,"imp":16.3890454014,"elo":1247.3,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"iLTM","family":"Foundation Model","x_train":410.883351941,"imp":16.2186243252,"elo":1345.3,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"iLTM","family":"Foundation Model","x_train":805.5219751868,"imp":13.5594974448,"elo":1442.5,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"iLTM","family":"Foundation Model","x_train":1992.2815124104,"imp":10.2704282323,"elo":1578.8,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"iLTM","family":"Foundation Model","x_train":2067.9528996121,"imp":10.2338269266,"elo":1584.4,"imputed":false,"imputed_pct":0.0,"n_configs":26.0},{"method":"xRFM","family":"Other","x_train":2.4940806965,"imp":25.9875577867,"elo":1051.9,"imputed":false,"imputed_pct":0.0,"n_configs":1.0},{"method":"xRFM","family":"Other","x_train":5.3133350441,"imp":21.3468517179,"elo":1127.7,"imputed":false,"imputed_pct":0.0,"n_configs":2.0},{"method":"xRFM","family":"Other","x_train":13.8020981623,"imp":16.1353961932,"elo":1255.0,"imputed":false,"imputed_pct":0.0,"n_configs":5.0},{"method":"xRFM","family":"Other","x_train":28.3270720096,"imp":13.9002791385,"elo":1372.9,"imputed":false,"imputed_pct":0.0,"n_configs":10.0},{"method":"xRFM","family":"Other","x_train":70.6047440508,"imp":12.9743181235,"elo":1424.6,"imputed":false,"imputed_pct":0.0,"n_configs":25.0},{"method":"xRFM","family":"Other","x_train":140.5453323732,"imp":12.6248944307,"elo":1441.4,"imputed":false,"imputed_pct":0.0,"n_configs":50.0},{"method":"xRFM","family":"Other","x_train":281.1244162931,"imp":12.4891082036,"elo":1446.3,"imputed":false,"imputed_pct":0.0,"n_configs":100.0},{"method":"xRFM","family":"Other","x_train":422.3532460518,"imp":12.4480188978,"elo":1447.6,"imputed":false,"imputed_pct":0.0,"n_configs":150.0},{"method":"xRFM","family":"Other","x_train":566.7369825686,"imp":12.4628187149,"elo":1446.1,"imputed":false,"imputed_pct":0.0,"n_configs":201.0}];
338
-
339
-
340
- const NS = "http://www.w3.org/2000/svg";
341
- // Baseline and Other are one bucket, as in the site's own type legend: they
342
- // already share a color, and each holds only a handful of methods.
343
- const FAM_MERGED = "Baseline / Other";
344
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
345
- // Normalized up front so every later lookup — colors, chips, sorting — sees
346
- // the merged family. Both templates declare POINTS above this block.
347
- for (const p of POINTS) p.family = famOf(p.family);
348
-
349
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
350
- const FAM_VAR = {
351
- "Foundation Model": "var(--fam-foundation)",
352
- "Tree-based": "var(--fam-tree)",
353
- "Neural Network": "var(--fam-nn)",
354
- "Reference Pipeline": "var(--fam-reference)",
355
- [FAM_MERGED]: "var(--fam-baseline)",
356
- };
357
- // The symbol the website shows for each family, so a family chip here reads the
358
- // same as the Type column on the site. Baseline and Other are one bucket, so
359
- // that chip carries both symbols.
360
- const FAM_SYMBOL = {
361
- "Foundation Model": "🧠⚡",
362
- "Tree-based": "🌳",
363
- "Neural Network": "🧠🔁",
364
- "Reference Pipeline": "📊",
365
- [FAM_MERGED]: "📏 ❓",
366
- };
367
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
368
- const VARIANT_VAR = {
369
- "Default": "var(--var-default)",
370
- "Tuned": "var(--var-tuned)",
371
- "Tuned + Ens.": "var(--var-tunedens)",
372
- };
373
-
374
- // A family chip's label: its symbol, its name and how many methods it holds.
375
- function famChipLabel(family, count) {
376
- const symbol = FAM_SYMBOL[family];
377
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
378
- family + ' <span class="count">&times;' + count + "</span>";
379
- }
380
-
381
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
382
- const FAM_INK = {
383
- "Foundation Model": "var(--fam-foundation-ink)",
384
- "Tree-based": "var(--fam-tree-ink)",
385
- "Neural Network": "var(--fam-nn-ink)",
386
- "Reference Pipeline": "var(--fam-reference-ink)",
387
- [FAM_MERGED]: "var(--fam-baseline-ink)",
388
- };
389
-
390
- // Create an SVG element with attributes, optionally appended to `parent`.
391
- function el(name, attrs, parent) {
392
- const node = document.createElementNS(NS, name);
393
- for (const k in attrs) node.setAttribute(k, attrs[k]);
394
- if (parent) parent.appendChild(node);
395
- return node;
396
- }
397
-
398
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
399
- // locale-independent by definition, which is the point: `toLocaleString`
400
- // would follow the *viewer's* browser locale and print 1234,5 for a German
401
- // visitor, disagreeing with the figures and CSVs beside it.
402
- function fmtNum(v, decimals) {
403
- if (v == null || !isFinite(v)) return "—";
404
- return v.toFixed(decimals);
405
- }
406
-
407
- function fmtMetric(metric, v) {
408
- if (v == null || !isFinite(v)) return "—";
409
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
410
- }
411
-
412
- function fmtTime(v) {
413
- if (v >= 100) return fmtNum(v, 0) + " s";
414
- if (v >= 1) return fmtNum(v, 1) + " s";
415
- if (v >= 0.1) return fmtNum(v, 2) + " s";
416
- return fmtNum(v, 3) + " s";
417
- }
418
-
419
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
420
- function niceStep(raw) {
421
- if (!(raw > 0)) return 1;
422
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
423
- for (const m of [1, 2, 2.5, 5, 10]) {
424
- if (mag * m >= raw) return mag * m;
425
- }
426
- return mag * 10;
427
- }
428
-
429
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
430
- // index rather than by accumulation so fractional steps do not drift.
431
- function ticks(min, max, target) {
432
- const step = niceStep((max - min) / target);
433
- const first = Math.ceil(min / step);
434
- const out = [];
435
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
436
- return out.length ? out : [min];
437
- }
438
-
439
- // A hover tooltip anchored inside `box` (which must be position:relative).
440
- function makeTooltip(box) {
441
- const node = box.querySelector(".tooltip");
442
- return {
443
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
444
- move(ev) {
445
- const r = box.getBoundingClientRect();
446
- let tx = ev.clientX - r.left + 14;
447
- const ty = ev.clientY - r.top + 12;
448
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
449
- node.style.left = tx + "px";
450
- node.style.top = ty + "px";
451
- },
452
- hide() { node.style.display = "none"; },
453
- };
454
- }
455
-
456
- // Paper view — white surface, legend + chart only — is the *default*: what a
457
- // reader wants first is the figure, and it is the state worth exporting. The
458
- // controls, chip list and data table are one click away behind "Edit view".
459
- // `afterToggle` re-renders charts whose size is measured from the layout.
460
- // `options.openInPaper` (default true) decides the state the page opens in. A
461
- // chart opens as the figure; the leaderboard table opens with its controls,
462
- // since there the interaction is the point rather than scaffolding around it.
463
- function setUpPaperView(afterToggle, options) {
464
- const opts = options || {};
465
- const root = document.documentElement;
466
- let hostTheme = null; // the embedding page's choice, captured on entry
467
- const btn = document.getElementById("btn-paper");
468
- const embedded = window.parent !== window;
469
-
470
- function setPaper(on) {
471
- document.body.classList.toggle("paper", on);
472
- if (on) {
473
- hostTheme = root.getAttribute("data-theme");
474
- root.setAttribute("data-theme", "light");
475
- } else if (hostTheme) {
476
- root.setAttribute("data-theme", hostTheme);
477
- } else {
478
- root.removeAttribute("data-theme");
479
- }
480
- btn.textContent = on ? "Edit view" : "Paper view";
481
- document.getElementById("exportbar").hidden = !on || embedded;
482
- if (afterToggle) requestAnimationFrame(afterToggle);
483
- postHeight();
484
- }
485
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
486
- // Embedded, the host page owns these controls — they sit beside the panel's
487
- // static-figure toggle and are driven from the outside. Standalone (the
488
- // shareable single file) this page needs its own.
489
- if (embedded) document.querySelector(".viewbar").hidden = true;
490
- window.addEventListener("message", ev => {
491
- const d = ev.data;
492
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
493
- });
494
- // Only standalone: embedded, the host owns the button and would not see the
495
- // key press, so its label would fall out of step with the frame.
496
- if (!embedded) {
497
- document.addEventListener("keydown", ev => {
498
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
499
- });
500
- }
501
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
502
- }
503
-
504
- // --- Figure export ---------------------------------------------------------
505
- // The chart is live SVG, so a file can be built from it directly. Three things
506
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
507
- // nothing outside this document), it has no background or font of its own, and
508
- // the legend is HTML rather than part of the SVG.
509
-
510
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
511
- // cloned and its label re-emitted at the measured position. foreignObject would
512
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
513
- // break the PNG path.
514
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
515
- // once the node leaves this document.
516
- function resolveVars(root, resolve) {
517
- for (const node of [root, ...root.querySelectorAll("*")]) {
518
- for (const attr of ["fill", "stroke"]) {
519
- const value = node.getAttribute(attr);
520
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
521
- }
522
- }
523
- }
524
-
525
- function legendToSvg(container, resolve) {
526
- const base = container.getBoundingClientRect();
527
- const group = document.createElementNS(NS, "g");
528
- let height = 0;
529
- for (const item of container.querySelectorAll(".item")) {
530
- const box = item.getBoundingClientRect();
531
- if (!box.width) continue;
532
- height = Math.max(height, box.bottom - base.top);
533
- let textLeft = box.left - base.left;
534
- const glyph = item.querySelector("svg");
535
- if (glyph) {
536
- const gbox = glyph.getBoundingClientRect();
537
- const wrap = el("g", {
538
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
539
- }, group);
540
- const glyphClone = glyph.cloneNode(true);
541
- resolveVars(glyphClone, resolve);
542
- wrap.appendChild(glyphClone);
543
- textLeft = gbox.right - base.left + 5;
544
- }
545
- const label = item.textContent.trim();
546
- if (!label) continue;
547
- const colored = item.querySelector("[style*='color']");
548
- const text = el("text", {
549
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
550
- fill: resolve(getComputedStyle(colored || item).color),
551
- }, group);
552
- text.textContent = label;
553
- }
554
- return { group, height: Math.ceil(height) };
555
- }
556
-
557
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
558
- // y-axis beside the scrolling plot) still exports as one figure.
559
- function buildExportSvg(parts, legendEl, pad = 10) {
560
- const rootStyle = getComputedStyle(document.documentElement);
561
- const resolve = value => String(value).replace(
562
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
563
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
564
-
565
- let chartW = 0, chartH = 0;
566
- for (const part of parts) {
567
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
568
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
569
- }
570
-
571
- const out = document.createElementNS(NS, "svg");
572
- out.setAttribute("xmlns", NS);
573
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
574
- let top = pad;
575
- const later = []; // built after the width is known
576
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
577
- if (legend && legend.height) {
578
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
579
- later.push(() => out.appendChild(legend.group));
580
- top += legend.height + 8;
581
- }
582
-
583
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
584
- const height = top + chartH + pad;
585
- out.setAttribute("width", Math.ceil(width));
586
- out.setAttribute("height", Math.ceil(height));
587
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
588
- for (const build of later) build();
589
-
590
- for (const part of parts) {
591
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
592
- const clone = part.svg.cloneNode(true);
593
- resolveVars(clone, resolve);
594
- while (clone.firstChild) group.appendChild(clone.firstChild);
595
- }
596
- return out;
597
- }
598
-
599
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
600
- function slugify(text) {
601
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
602
- }
603
-
604
- function downloadUrl(url, filename) {
605
- const a = document.createElement("a");
606
- a.href = url;
607
- a.download = filename;
608
- document.body.appendChild(a);
609
- a.click();
610
- a.remove();
611
- }
612
-
613
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
614
- function rasterize(svg, scale, done, fail) {
615
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
616
- const img = new Image();
617
- img.onload = () => {
618
- const canvas = document.createElement("canvas");
619
- canvas.width = Math.round(width * scale);
620
- canvas.height = Math.round(height * scale);
621
- const ctx = canvas.getContext("2d");
622
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
623
- ctx.drawImage(img, 0, 0);
624
- done(canvas, width, height);
625
- };
626
- img.onerror = fail;
627
- img.src = "data:image/svg+xml;charset=utf-8,"
628
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
629
- }
630
-
631
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
632
- // cost this page its zero-dependency, single-file property. The image is stored
633
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
634
- // in points to the figure's CSS size, so it prints at the size it appears here
635
- // and the pixels land at 96*scale dpi.
636
- async function buildPdf(canvas, cssWidth, cssHeight) {
637
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
638
- const rgb = new Uint8Array((pixels.length / 4) * 3);
639
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
640
- rgb[j] = pixels[i];
641
- rgb[j + 1] = pixels[i + 1];
642
- rgb[j + 2] = pixels[i + 2];
643
- }
644
- const deflated = new Uint8Array(await new Response(
645
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
646
-
647
- const encoder = new TextEncoder();
648
- const chunks = [];
649
- const offsets = [];
650
- let cursor = 0;
651
- const put = data => {
652
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
653
- chunks.push(bytes);
654
- cursor += bytes.length;
655
- };
656
- const object = (id, body, stream) => {
657
- offsets[id] = cursor;
658
- put(`${id} 0 obj\n${body}\n`);
659
- if (stream) {
660
- put("stream\n");
661
- put(stream);
662
- put("\nendstream\n");
663
- }
664
- put("endobj\n");
665
- };
666
-
667
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
668
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
669
- put("%PDF-1.4\n");
670
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
671
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
672
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
673
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
674
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
675
- object(4, "<< /Type /XObject /Subtype /Image "
676
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
677
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
678
- object(5, `<< /Length ${content.length} >>`, content);
679
-
680
- const xref = cursor;
681
- let table = "xref\n0 6\n0000000000 65535 f \n";
682
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
683
- put(table);
684
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
685
- return new Blob(chunks, { type: "application/pdf" });
686
- }
687
-
688
- // Wire up the export buttons; `getParts` is called per click so the file always
689
- // matches what is on screen. Returns a `run(format)` the host page can drive.
690
- function setUpExport(getParts, basename) {
691
- const buttons = {
692
- svg: document.getElementById("btn-svg"),
693
- png: document.getElementById("btn-png"),
694
- pdf: document.getElementById("btn-pdf"),
695
- };
696
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
697
-
698
- // A sandboxed frame has no modals, so a failure is reported on the button.
699
- function complain(format) {
700
- const button = buttons[format];
701
- if (!button) return;
702
- const label = button.textContent;
703
- button.textContent = "failed";
704
- setTimeout(() => { button.textContent = label; }, 2500);
705
- }
706
-
707
- function run(format) {
708
- const svg = figure();
709
- const name = basename();
710
- if (format === "svg") {
711
- downloadUrl("data:image/svg+xml;charset=utf-8,"
712
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
713
- return;
714
- }
715
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
716
- // points so the pixels already land near 200 dpi at print size.
717
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
718
- if (format === "png") {
719
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
720
- } else {
721
- buildPdf(canvas, cssWidth, cssHeight)
722
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
723
- .catch(() => complain("pdf"));
724
- }
725
- }, () => complain(format));
726
- }
727
-
728
- for (const format of Object.keys(buttons)) {
729
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
730
- }
731
- // Embedded, the buttons live in the host's panel header (see main.taExport).
732
- window.addEventListener("message", ev => {
733
- const d = ev.data;
734
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
735
- });
736
- }
737
-
738
- // When embedded, report the content height so the host page can size the
739
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
740
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
741
- // clamped to at least the iframe's current viewport, which turns the
742
- // resize round-trip into a grow-forever feedback loop. The change guard
743
- // stops re-posting once the height settles.
744
- let lastPostedHeight = 0;
745
- function postHeight() {
746
- if (window.parent === window) return;
747
- const height = Math.ceil(document.body.offsetHeight);
748
- if (Math.abs(height - lastPostedHeight) < 3) return;
749
- lastPostedHeight = height;
750
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
751
- }
752
-
753
-
754
- const TRAJECTORY = CONFIG.mode === "trajectory";
755
-
756
- const titleEl = document.getElementById("title");
757
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
758
-
759
- // Column order: chips/controls left of the chart, or mirrored.
760
- document.getElementById("grid").classList.add(CONFIG.chipsSide === "right" ? "chips-right" : "chips-left");
761
-
762
- const svg = document.getElementById("chart");
763
- const box = document.getElementById("chartbox");
764
- const chipsBox = document.getElementById("chips");
765
- const controlsBox = document.querySelector(".controls");
766
- const tip = makeTooltip(box);
767
-
768
- // Marker glyph per variant at (cx, cy); trajectories use circles everywhere.
769
- function drawMark(parent, cx, cy, variant, color, size, opacity, dataM, whiteStroke) {
770
- const common = { opacity: opacity, "data-m": dataM };
771
- let node;
772
- if (TRAJECTORY || variant === "Default" || !variant) {
773
- node = el("circle", { ...common, cx, cy, r: size, fill: color }, parent);
774
- } else if (variant === "Tuned") {
775
- const s = size * 1.75;
776
- node = el("rect", { ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1.5, fill: color }, parent);
777
- } else if (variant === "Tuned + Ens.") {
778
- const d = size * 0.95;
779
- node = el("path", {
780
- ...common,
781
- d: `M${cx - d},${cy - d} L${cx + d},${cy + d} M${cx - d},${cy + d} L${cx + d},${cy - d}`,
782
- stroke: color, "stroke-width": size * 0.62, fill: "none", "stroke-linecap": "round",
783
- }, parent);
784
- } else {
785
- // Any other variant (e.g. "Baseline", holdout types): diamond.
786
- const s = size * 1.45;
787
- node = el("rect", {
788
- ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1,
789
- fill: color, transform: `rotate(45 ${cx} ${cy})`,
790
- }, parent);
791
- }
792
- if (whiteStroke && variant !== "Tuned + Ens.") {
793
- node.setAttribute("stroke", "var(--card)");
794
- node.setAttribute("stroke-width", "1");
795
- }
796
- return node;
797
- }
798
-
799
- function drawImputedRing(parent, cx, cy, size, color, opacity, dataM) {
800
- el("circle", {
801
- cx, cy, r: size + 4.5, fill: "none", stroke: color, "stroke-width": 1.4,
802
- "stroke-dasharray": "3 2.5", opacity: opacity, "data-m": dataM,
803
- }, parent);
804
- }
805
-
806
- // Fat green arrow pointing into the optimal corner (mirrors the static
807
- // figures' "Optimal" arrow so both read the same way).
808
- function drawOptimalArrow(parent, lowerBetter, M, W, H) {
809
- const cornerY = lowerBetter ? H - M.b - 12 : M.t + 12;
810
- const tailY = lowerBetter ? H - M.b - 64 : M.t + 64;
811
- const cx = M.l + 12, tx = M.l + 64;
812
- const dx = cx - tx, dy = cornerY - tailY;
813
- const len = Math.hypot(dx, dy);
814
- const ux = dx / len, uy = dy / len;
815
- const headLen = 16;
816
- const bx = cx - ux * headLen, by = cornerY - uy * headLen; // head base center
817
- // line stops at the head base
818
- el("line", {
819
- x1: tx, y1: tailY, x2: bx, y2: by,
820
- stroke: "var(--optimal)", "stroke-width": 13, "stroke-linecap": "round", opacity: 0.92,
821
- }, parent);
822
- const px = -uy, py = ux; // perpendicular
823
- el("polygon", {
824
- points: `${cx},${cornerY} ${bx + px * 11},${by + py * 11} ${bx - px * 11},${by - py * 11}`,
825
- fill: "var(--optimal)", opacity: 0.92,
826
- }, parent);
827
- let angle = Math.atan2(dy, dx) * 180 / Math.PI;
828
- if (angle > 90 || angle < -90) angle += 180;
829
- const mx = (tx + bx) / 2, my = (tailY + by) / 2;
830
- const t = el("text", {
831
- x: mx, y: my, "text-anchor": "middle", "dominant-baseline": "middle",
832
- "font-size": 10.5, "font-weight": 700, fill: "#ffffff",
833
- transform: `rotate(${angle} ${mx} ${my})`,
834
- }, parent);
835
- t.textContent = "Optimal";
836
- }
837
-
838
- // ---------- data ----------
839
- const byMethod = new Map();
840
- for (const p of POINTS) {
841
- if (!byMethod.has(p.method)) byMethod.set(p.method, []);
842
- byMethod.get(p.method).push(p); // insertion order = builder's point order
843
- }
844
-
845
- const METRICS = CONFIG.metrics;
846
- let metricKey = METRICS[0].key;
847
- const metricByKey = {};
848
- for (const m of METRICS) metricByKey[m.key] = m;
849
-
850
- // Single time axis per explorer (the scatter ships inference time, the
851
- // trajectories train time).
852
- const X_AXIS = CONFIG.xAxes[0];
853
- const xKey = X_AXIS.key;
854
-
855
- function mval(p, metric) { return p[metric.key]; }
856
-
857
- function computeFront(metric) {
858
- const xk = xKey;
859
- const pts = [...POINTS].sort((a, b) =>
860
- a[xk] - b[xk] || (metric.lowerBetter ? mval(a, metric) - mval(b, metric) : mval(b, metric) - mval(a, metric)));
861
- const verts = [];
862
- const methods = new Set();
863
- let best = null;
864
- for (const p of pts) {
865
- const v = mval(p, metric);
866
- if (best === null || (metric.lowerBetter ? v < best : v > best)) {
867
- if (best !== null) verts.push([p[xk], best]);
868
- verts.push([p[xk], v]);
869
- best = v;
870
- methods.add(p.method);
871
- }
872
- }
873
- return { verts, methods };
874
- }
875
-
876
- const state = { active: new Set(computeFront(metricByKey[metricKey]).methods) };
877
-
878
- // ---------- chart ----------
879
- // A flat, fixed-height plot: two of these panels then fit on one screen.
880
- const CHART_H = 400;
881
- const M = { l: 62, r: 18, t: 14, b: 52 };
882
-
883
- function render() {
884
- const metric = metricByKey[metricKey];
885
- svg.textContent = "";
886
- const W = Math.max(360, Math.round(box.clientWidth));
887
- const H = CHART_H;
888
- svg.setAttribute("width", W);
889
- svg.setAttribute("height", H);
890
- // Keep the chip list from outgrowing the chart beside it.
891
- chipsBox.style.maxHeight = Math.max(170, H - controlsBox.offsetHeight + 20) + "px";
892
-
893
- // x scale (log)
894
- const xsAll = POINTS.map(p => p[xKey]);
895
- const xmin = Math.min(...xsAll) * 0.65, xmax = Math.max(...xsAll) * 1.6;
896
- const lx0 = Math.log10(xmin), lx1 = Math.log10(xmax);
897
- const X = v => M.l + (Math.log10(v) - lx0) / (lx1 - lx0) * (W - M.l - M.r);
898
-
899
- const vals = POINTS.map(p => mval(p, metric));
900
- let y0, y1;
901
- if (metric.fromZero) {
902
- y0 = 0; y1 = Math.max(...vals) * 1.07;
903
- } else {
904
- const pad = (Math.max(...vals) - Math.min(...vals)) * 0.07;
905
- y0 = Math.min(...vals) - pad; y1 = Math.max(...vals) + pad;
906
- }
907
- const Y = v => M.t + (1 - (v - y0) / (y1 - y0)) * (H - M.t - M.b);
908
-
909
- // grid + axes
910
- const grid = el("g", {}, svg);
911
- for (let e = Math.ceil(lx0); Math.pow(10, e) < xmax; e++) {
912
- const gx = X(Math.pow(10, e));
913
- el("line", { x1: gx, y1: M.t, x2: gx, y2: H - M.b, stroke: "var(--line)", "stroke-width": 1 }, grid);
914
- const lbl = fmtNum(Math.pow(10, e), e >= 0 ? 0 : -e);
915
- el("text", { x: gx, y: H - M.b + 20, "text-anchor": "middle", "font-size": 12.5, fill: "var(--muted)" }, grid)
916
- .textContent = lbl;
917
- }
918
- for (const yv of ticks(y0, y1, 6)) {
919
- const gy = Y(yv);
920
- el("line", { x1: M.l, y1: gy, x2: W - M.r, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
921
- el("text", { x: M.l - 8, y: gy + 4, "text-anchor": "end", "font-size": 12.5, fill: "var(--muted)" }, grid)
922
- .textContent = fmtNum(yv, Number.isInteger(yv) ? 0 : metric.decimals);
923
- }
924
- el("rect", { x: M.l, y: M.t, width: W - M.l - M.r, height: H - M.t - M.b, fill: "none", stroke: "var(--line)" }, grid);
925
- el("text", {
926
- x: (M.l + W - M.r) / 2, y: H - 10, "text-anchor": "middle", "font-size": 14,
927
- "font-weight": 650, fill: "var(--ink)",
928
- }, grid).textContent = X_AXIS.axisLabel;
929
- el("text", {
930
- x: 0, y: 0, "text-anchor": "middle", "font-size": 14, "font-weight": 650, fill: "var(--ink)",
931
- transform: `translate(16 ${(M.t + H - M.b) / 2}) rotate(-90)`,
932
- }, grid).textContent = metric.axisLabel;
933
-
934
- drawOptimalArrow(grid, metric.lowerBetter, M, W, H);
935
-
936
- // pareto front (always shown)
937
- const front = computeFront(metric);
938
- const fv = front.verts;
939
- if (fv.length) {
940
- let d = `M${X(fv[0][0])},${metric.lowerBetter ? M.t : H - M.b}`;
941
- for (const [fx, fy] of fv) d += ` L${X(fx)},${Y(fy)}`;
942
- d += ` L${W - M.r},${Y(fv[fv.length - 1][1])}`;
943
- el("path", { d, fill: "none", stroke: "var(--ink)", "stroke-width": 1.6, "stroke-dasharray": "7 5", opacity: 0.85 }, svg);
944
- }
945
-
946
- const isOn = m => state.active.has(m);
947
-
948
- // connectors: variant links (scatter) / the trajectory itself
949
- const conn = el("g", {}, svg);
950
- for (const [method, pts] of byMethod) {
951
- if (pts.length < 2) continue;
952
- const on = isOn(method);
953
- if (!TRAJECTORY && !on) continue; // scatter: connectors only for active methods
954
- const dd = pts.map((p, i) => `${i ? "L" : "M"}${X(p[xKey])},${Y(mval(p, metric))}`).join(" ");
955
- el("path", {
956
- d: dd, fill: "none",
957
- stroke: on ? FAM_VAR[pts[0].family] : "var(--pt-muted)",
958
- "stroke-width": on ? (TRAJECTORY ? 2 : 1.4) : 1,
959
- opacity: on ? 0.6 : 0.35,
960
- "data-m": method,
961
- }, conn);
962
- }
963
-
964
- // points: inactive first, active on top
965
- const ptsOff = el("g", {}, svg);
966
- const ptsOn = el("g", {}, svg);
967
- for (const [method, pts] of byMethod) {
968
- const on = isOn(method);
969
- for (const p of pts) {
970
- const color = on ? FAM_VAR[p.family] : "var(--pt-muted)";
971
- const size = (on ? 7 : 5) * (TRAJECTORY ? 0.8 : 1);
972
- const op = on ? 0.95 : 0.5;
973
- drawMark(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), p.variant, color, size, op, p.method, on);
974
- // Imputation ring: every affected point in scatter mode; only the
975
- // trajectory's end point in trajectory mode (a ring on all ~8 line
976
- // points would read as beads, and the chip's ‡ already flags the line).
977
- if (p.imputed && (!TRAJECTORY || p === pts[pts.length - 1])) {
978
- drawImputedRing(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), size, color, op, p.method);
979
- }
980
- }
981
- }
982
-
983
- // labels for active methods at their best point, greedy de-overlap
984
- const labels = [];
985
- for (const [method, pts] of byMethod) {
986
- if (!isOn(method)) continue;
987
- const best = pts.reduce((a, b) =>
988
- (metric.lowerBetter ? mval(a, metric) < mval(b, metric) : mval(a, metric) > mval(b, metric)) ? a : b);
989
- labels.push({ method, family: best.family, x: X(best[xKey]) + 10, y: Y(mval(best, metric)) - 10 });
990
- }
991
- labels.sort((a, b) => a.y - b.y);
992
- for (let i = 1; i < labels.length; i++) {
993
- for (let j = 0; j < i; j++) {
994
- if (Math.abs(labels[i].x - labels[j].x) < 110 && Math.abs(labels[i].y - labels[j].y) < 15) {
995
- labels[i].y = labels[j].y + 15;
996
- }
997
- }
998
- }
999
- const lg = el("g", {}, svg);
1000
- for (const l of labels) {
1001
- const t = el("text", {
1002
- x: Math.min(l.x, W - M.r - 8), y: Math.max(l.y, M.t + 12), "font-size": 13, "font-weight": 700,
1003
- fill: FAM_VAR[l.family], "paint-order": "stroke", stroke: "var(--card)", "stroke-width": 3.5,
1004
- "text-anchor": l.x > W - 120 ? "end" : "start",
1005
- }, lg);
1006
- t.textContent = l.method;
1007
- }
1008
-
1009
- // invisible hit targets on top (bigger than marks)
1010
- const hits = el("g", {}, svg);
1011
- for (const p of POINTS) {
1012
- const h = el("circle", { cx: X(p[xKey]), cy: Y(mval(p, metric)), r: 12, fill: "transparent", cursor: "pointer" }, hits);
1013
- h.addEventListener("mouseenter", ev => showTip(p, ev));
1014
- h.addEventListener("mousemove", ev => tip.move(ev));
1015
- h.addEventListener("mouseleave", () => hideTip(p.method));
1016
- h.addEventListener("click", () => toggle(p.method));
1017
- }
1018
- }
1019
-
1020
- // Temporary hover emphasis without a re-render (a re-render would replace
1021
- // the hit node under the cursor mid-hover).
1022
- function emphasize(method, on) {
1023
- svg.querySelectorAll(`[data-m="${CSS.escape(method)}"]`).forEach(n => {
1024
- if (on) {
1025
- if (!n.dataset.save) n.dataset.save = n.getAttribute("opacity") || "1";
1026
- n.setAttribute("opacity", "0.95");
1027
- } else if (n.dataset.save) {
1028
- n.setAttribute("opacity", n.dataset.save);
1029
- delete n.dataset.save;
1030
- }
1031
- });
1032
- }
1033
-
1034
- function showTip(p, ev) {
1035
- emphasize(p.method, true);
1036
- const sub = TRAJECTORY ? (p.n_configs != null ? `${p.n_configs} configs` : "") : (p.variant || "");
1037
- let html = `<div class="t-name">${p.method}` + (sub ? ` <span class="t-var">(${sub})</span>` : "") + "</div>" +
1038
- `<div>${p.family}</div>`;
1039
- for (const m of METRICS) {
1040
- html += `<div>${m.label}: <b>${fmtMetric(m, mval(p, m))}</b></div>`;
1041
- }
1042
- html += `<div>${X_AXIS.short}: <b>${fmtTime(p[xKey])}</b></div>`;
1043
- if (p.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(p.imputed_pct, 0)}% of datasets</div>`;
1044
- tip.show(html, ev);
1045
- }
1046
- function hideTip(method) {
1047
- emphasize(method, false);
1048
- tip.hide();
1049
- }
1050
-
1051
- // ---------- chips ----------
1052
- const chipByMethod = new Map();
1053
- const famChips = new Map();
1054
- function familyMethods(fam) {
1055
- return [...byMethod.keys()].filter(m => byMethod.get(m)[0].family === fam);
1056
- }
1057
- // Chips are listed by leaderboard rank — best Elo first when Elo is
1058
- // configured, otherwise best value of the primary metric.
1059
- const RANK_METRIC = metricByKey["elo"] || metricByKey[METRICS[0].key];
1060
- function bestVal(method) {
1061
- const vals = byMethod.get(method).map(p => mval(p, RANK_METRIC));
1062
- return RANK_METRIC.lowerBetter ? Math.min(...vals) : Math.max(...vals);
1063
- }
1064
- function rankSorted(methods) {
1065
- return [...methods].sort((a, b) =>
1066
- RANK_METRIC.lowerBetter ? bestVal(a) - bestVal(b) : bestVal(b) - bestVal(a));
1067
- }
1068
- function buildChips() {
1069
- for (const fam of FAM_ORDER) {
1070
- const methods = rankSorted(familyMethods(fam));
1071
- if (!methods.length) continue;
1072
- const row = document.createElement("div");
1073
- row.className = "chiprow";
1074
- const famBtn = document.createElement("button");
1075
- famBtn.className = "famchip";
1076
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1077
- famBtn.innerHTML = famChipLabel(fam, methods.length);
1078
- famBtn.title = `Toggle all ${methods.length} ${fam} methods`;
1079
- famBtn.addEventListener("click", () => toggleFamily(fam));
1080
- row.appendChild(famBtn);
1081
- famChips.set(fam, famBtn);
1082
- const set = document.createElement("div");
1083
- set.className = "chipset";
1084
- for (const m of methods) {
1085
- const b = document.createElement("button");
1086
- b.className = "chip";
1087
- b.style.setProperty("--fam", FAM_VAR[fam]);
1088
- const imputed = byMethod.get(m).some(p => p.imputed);
1089
- const label = document.createElement("span");
1090
- label.textContent = m;
1091
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1092
- b.appendChild(label);
1093
- if (imputed) {
1094
- const mark = document.createElement("span");
1095
- mark.className = "imp-mark";
1096
- mark.textContent = "‡";
1097
- b.appendChild(mark);
1098
- }
1099
- b.title = m + (imputed ? " — partially imputed" : "");
1100
- b.addEventListener("click", () => toggle(m));
1101
- set.appendChild(b);
1102
- chipByMethod.set(m, b);
1103
- }
1104
- row.appendChild(set);
1105
- chipsBox.appendChild(row);
1106
- }
1107
- }
1108
- function syncChips() {
1109
- for (const [m, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.active.has(m)));
1110
- for (const [fam, b] of famChips) {
1111
- b.setAttribute("aria-pressed", String(familyMethods(fam).every(m => state.active.has(m))));
1112
- }
1113
- }
1114
- function toggle(m) {
1115
- if (state.active.has(m)) state.active.delete(m); else state.active.add(m);
1116
- syncChips();
1117
- render();
1118
- }
1119
- function toggleFamily(fam) {
1120
- const methods = familyMethods(fam);
1121
- const allOn = methods.every(m => state.active.has(m));
1122
- for (const m of methods) {
1123
- if (allOn) state.active.delete(m); else state.active.add(m);
1124
- }
1125
- syncChips();
1126
- render();
1127
- }
1128
- function setActive(methods) {
1129
- state.active = new Set(methods);
1130
- syncChips();
1131
- render();
1132
- }
1133
- document.getElementById("btn-front").addEventListener("click",
1134
- () => setActive(computeFront(metricByKey[metricKey]).methods));
1135
- document.getElementById("btn-all").addEventListener("click", () => setActive([...byMethod.keys()]));
1136
- document.getElementById("btn-none").addEventListener("click", () => setActive([]));
1137
-
1138
- // metric selector (hidden when only one metric is configured)
1139
- const metricPick = document.getElementById("metricpick");
1140
- const metricSelect = document.getElementById("metric-select");
1141
- if (METRICS.length > 1) {
1142
- metricPick.hidden = false;
1143
- for (const m of METRICS) {
1144
- const opt = document.createElement("option");
1145
- opt.value = m.key;
1146
- opt.textContent = m.label;
1147
- metricSelect.appendChild(opt);
1148
- }
1149
- metricSelect.addEventListener("change", ev => {
1150
- metricKey = ev.target.value;
1151
- render();
1152
- });
1153
- }
1154
-
1155
- // ---------- legend strip ----------
1156
- function buildLegend() {
1157
- const box2 = document.getElementById("legendstrip");
1158
- let html = "";
1159
- if (!TRAJECTORY) {
1160
- html +=
1161
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><circle cx="7" cy="7" r="5" fill="var(--muted)"/></svg> Default</span>' +
1162
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" rx="1.5" fill="var(--muted)"/></svg> Tuned</span>' +
1163
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><path d="M3,3 L11,11 M3,11 L11,3" stroke="var(--muted)" stroke-width="2.6" stroke-linecap="round"/></svg> Tuned + Ensembled</span>';
1164
- } else {
1165
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--muted)" stroke-width="2"/><circle cx="6" cy="4" r="2.6" fill="var(--muted)"/><circle cx="16" cy="4" r="2.6" fill="var(--muted)"/></svg> Tuning trajectory (more configs &rarr; more time)</span>';
1166
- }
1167
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--ink)" stroke-width="1.6" stroke-dasharray="6 4"/></svg> Pareto front (always shown)</span>';
1168
- if (POINTS.some(p => p.imputed)) {
1169
- html += '<span class="item"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="4" fill="var(--muted)"/><circle cx="9" cy="9" r="7.5" fill="none" stroke="var(--muted)" stroke-width="1.3" stroke-dasharray="3 2.5"/></svg> &Dagger; partially imputed</span>';
1170
- }
1171
- // Model family, named: highlighted points are colored by family, and in paper
1172
- // view the chip list that would otherwise decode them is hidden.
1173
- const families = FAM_ORDER.filter(f => POINTS.some(p => p.family === f));
1174
- if (families.length > 1) {
1175
- html += '<span class="legendbreak"></span><span class="item">Family:</span>';
1176
- for (const fam of families) {
1177
- html += `<span class="item"><svg width="12" height="12" viewBox="0 0 12 12">` +
1178
- `<circle cx="6" cy="6" r="5" fill="${FAM_VAR[fam]}"/></svg> ` +
1179
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`;
1180
- }
1181
- }
1182
- box2.innerHTML = html;
1183
- }
1184
-
1185
- // ---------- paper view ----------
1186
- setUpPaperView(render);
1187
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(document.title));
1188
-
1189
- // ---------- data table ----------
1190
- function buildTable() {
1191
- const m0 = metricByKey[METRICS[0].key];
1192
- const rows = [...POINTS].sort((a, b) =>
1193
- m0.lowerBetter ? mval(a, m0) - mval(b, m0) : mval(b, m0) - mval(a, m0));
1194
- let html = "<table><thead><tr><th>Method</th>";
1195
- html += TRAJECTORY ? "<th>Configs</th>" : "<th>Variant</th>";
1196
- html += "<th>Family</th>";
1197
- for (const m of METRICS) html += `<th>${m.label}</th>`;
1198
- html += `<th>${X_AXIS.short}</th><th>Imputed</th></tr></thead><tbody>`;
1199
- for (const p of rows) {
1200
- html += `<tr><td>${p.method}</td><td>${TRAJECTORY ? (p.n_configs != null ? p.n_configs : "—") : p.variant}</td><td>${p.family}</td>`;
1201
- for (const m of METRICS) html += `<td>${fmtMetric(m, mval(p, m))}</td>`;
1202
- html += `<td>${fmtNum(p[xKey], 3)}</td>`;
1203
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1204
- }
1205
- html += "</tbody></table>";
1206
- document.getElementById("tblwrap").innerHTML = html;
1207
- }
1208
-
1209
- const _renderInner = render;
1210
- render = function () {
1211
- _renderInner();
1212
- postHeight();
1213
- };
1214
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1215
- let resizeTimer = null;
1216
- window.addEventListener("resize", () => {
1217
- clearTimeout(resizeTimer);
1218
- resizeTimer = setTimeout(render, 120);
1219
- });
1220
- window.addEventListener("load", postHeight);
1221
-
1222
- buildChips();
1223
- buildLegend();
1224
- buildTable();
1225
- syncChips();
1226
- render();
1227
- })();
1228
- </script>
1229
- </body>
1230
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/website_leaderboard.csv DELETED
@@ -1,71 +0,0 @@
1
- #,Type,TypeName,Model,Elo [⬆️],Elo 95% CI,Score [⬆️],Rank [⬇️],Harmonic Rank [⬇️],Improvability (%) [⬇️],Median Train Time (s/1K) [⬇️],Median Predict Time (s/1K) [⬇️],Verified,Imputed (%) [⬇️],Imputed,Hardware
2
- 0,🧠⚡,Foundation Model,[TabFM (default)](https://github.com/google-research/tabfm),1986.0,+306/-145,0.867,4.41,1.69,2.683,27.29,13.119,✔️,0.0,False,GPU
3
- 1,📊,Reference Pipeline,"[AutoGluon 1.5 (extreme, 4h)](https://arxiv.org/abs/2003.06505)",1899.0,+85/-49,0.71,6.33,2.98,4.858,191.18,2.207,✔️,0.0,False,GPU
4
- 2,🧠⚡,Foundation Model,[TabPFN-3 (default)](https://arxiv.org/abs/2605.13986),1794.0,+214/-135,0.659,9.55,3.26,5.878,1.01,0.215,✔️,0.0,False,GPU
5
- 3,📊,Reference Pipeline,"[AutoGluon 1.4 (best, 4h)](https://arxiv.org/abs/2003.06505)",1775.0,+83/-51,0.579,10.23,4.46,6.048,432.36,4.085,✔️,0.0,False,CPU
6
- 4,🧠⚡,Foundation Model,[TabPFN-2.6 (default)](https://arxiv.org/abs/2511.08667),1722.0,+130/-75,0.504,12.39,8.78,7.846,2.76,0.701,✔️,0.0,False,GPU
7
- 5,🧠⚡,Foundation Model,[TabICLv2 (default)](https://arxiv.org/abs/2602.11139),1697.0,+186/-110,0.541,13.53,4.41,6.851,0.6,0.119,✔️,0.0,False,GPU
8
- 6,🧠⚡,Foundation Model,[RealTabPFN-2.5 (tuned + ensembled)](https://arxiv.org/abs/2511.08667),1646.0,+142/-104,0.446,16.07,8.48,8.576,735.58,11.736,✔️,0.0,False,GPU
9
- 7,🧠🔁,Neural Network,[RealMLP (tuned + ensembled)](https://arxiv.org/abs/2407.04491),1625.0,+92/-92,0.412,17.17,11.86,8.917,1719.82,1.675,✔️,0.0,False,GPU
10
- 8,🧠🔁,Neural Network,[ModernNCA (tuned + ensembled)](https://arxiv.org/abs/2407.03257),1615.0,+104/-105,0.397,17.73,12.25,9.07,2526.31,6.013,✔️,0.0,False,GPU
11
- 9,🌳,Tree-based,[CatBoost (tuned + ensembled)](https://arxiv.org/abs/1706.09516),1611.0,+85/-63,0.379,17.97,9.32,8.932,489.93,0.249,✔️,0.0,False,CPU
12
- 10,🧠⚡,Foundation Model,[RealTabPFN-2.5 (tuned)](https://arxiv.org/abs/2511.08667),1600.0,+126/-99,0.403,18.55,10.47,9.218,735.58,1.386,✔️,0.0,False,GPU
13
- 11,🌳,Tree-based,[CatBoost (tuned)](https://arxiv.org/abs/1706.09516),1596.0,+98/-66,0.372,18.8,9.43,9.14,489.93,0.02,✔️,0.0,False,CPU
14
- 12,🌳,Tree-based,[LightGBM (tuned + ensembled)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1589.0,+68/-51,0.327,19.19,13.5,10.466,131.56,2.639,✔️,0.0,False,CPU
15
- 13,🌳,Tree-based,[CatBoost (default)](https://arxiv.org/abs/1706.09516),1576.0,+103/-101,0.378,19.95,9.61,9.095,2.47,0.01,✔️,0.0,False,CPU
16
- 14,🧠⚡,Foundation Model,[iLTM (tuned + ensembled)](https://arxiv.org/abs/2511.15941),1558.0,+70/-52,0.322,20.99,15.67,10.221,2067.95,65.195,✔️,0.0,False,GPU
17
- 15,🌳,Tree-based,[XGBoost (tuned + ensembled)](https://arxiv.org/abs/1603.02754),1548.0,+81/-55,0.295,21.61,14.86,10.546,282.13,0.563,✔️,0.0,False,CPU
18
- 16,🧠🔁,Neural Network,[TabM (tuned + ensembled)](https://arxiv.org/abs/2410.24210),1544.0,+146/-87,0.292,21.87,13.14,10.254,1236.88,0.579,✔️,0.0,False,GPU
19
- 17,🧠🔁,Neural Network,[RealMLP (tuned)](https://arxiv.org/abs/2407.04491),1533.0,+105/-89,0.301,22.52,14.08,10.104,1719.82,0.081,✔️,0.0,False,GPU
20
- 18,🧠⚡,Foundation Model,[RealTabPFN-2.5 (default)](https://arxiv.org/abs/2511.08667),1530.0,+136/-79,0.324,22.69,14.72,10.077,1.88,0.645,✔️,0.0,False,GPU
21
- 19,🧠⚡,Foundation Model,[TabDPT-Turbo (default)](https://openreview.net/pdf?id=Y00pwFyrHR),1526.0,+156/-139,0.355,22.94,10.98,10.809,0.75,0.18,➖,0.0,False,GPU
22
- 20,🧠⚡,Foundation Model,[TabDPT (tuned + ensembled)](https://arxiv.org/abs/2410.18164),1522.0,+134/-119,0.319,23.22,6.34,10.287,4786.6,444.544,✔️,0.0,False,GPU
23
- 21,🌳,Tree-based,[ChimeraBoost (tuned + ensembled)](https://github.com/bbstats/chimeraboost),1517.0,+116/-121,0.314,23.53,13.31,11.684,95.92,0.297,✔️,0.0,False,CPU
24
- 22,🌳,Tree-based,[LightGBM (tuned)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1506.0,+76/-54,0.216,24.2,18.44,11.603,131.56,0.132,✔️,0.0,False,CPU
25
- 23,🌳,Tree-based,[XGBoost (tuned)](https://arxiv.org/abs/1603.02754),1501.0,+64/-55,0.234,24.53,20.07,11.102,282.13,0.066,✔️,0.0,False,CPU
26
- 24,🧠🔁,Neural Network,[TabM (tuned)](https://arxiv.org/abs/2410.24210),1481.0,+130/-76,0.234,25.86,15.63,11.089,1236.88,0.054,✔️,0.0,False,GPU
27
- 25,🧠🔁,Neural Network,[ModernNCA (tuned)](https://arxiv.org/abs/2407.03257),1472.0,+92/-81,0.217,26.42,20.56,11.215,2526.31,0.349,✔️,0.0,False,GPU
28
- 26,🌳,Tree-based,[ChimeraBoost (tuned)](https://github.com/bbstats/chimeraboost),1470.0,+106/-115,0.266,26.58,15.47,12.146,95.92,0.027,✔️,0.0,False,CPU
29
- 27,❓,Other,[xRFM (tuned + ensembled)](https://arxiv.org/abs/2508.10053),1423.0,+106/-114,0.224,29.66,21.18,12.482,566.74,0.951,✔️,0.0,False,GPU
30
- 28,🧠⚡,Foundation Model,[iLTM (tuned)](https://arxiv.org/abs/2511.15941),1423.0,+97/-63,0.155,29.69,18.8,12.112,2067.95,10.881,✔️,0.0,False,GPU
31
- 29,🌳,Tree-based,[ChimeraBoost (default)](https://github.com/bbstats/chimeraboost),1418.0,+112/-119,0.203,30.0,19.33,13.176,0.48,0.023,✔️,0.0,False,CPU
32
- 30,🧠⚡,Foundation Model,[TabDPT (tuned)](https://arxiv.org/abs/2410.18164),1404.0,+123/-118,0.221,30.97,12.53,12.662,4786.6,42.641,✔️,0.0,False,GPU
33
- 31,🧠🔁,Neural Network,[TorchMLP (tuned + ensembled)](https://arxiv.org/abs/2003.06505),1377.0,+71/-61,0.128,32.79,26.34,12.45,1770.56,1.069,✔️,0.0,False,CPU
34
- 32,❓,Other,[xRFM (tuned)](https://arxiv.org/abs/2508.10053),1373.0,+92/-101,0.159,33.01,27.27,13.982,566.74,0.06,✔️,0.0,False,GPU
35
- 33,🌳,Tree-based,[XGBoost (default)](https://arxiv.org/abs/1603.02754),1372.0,+89/-103,0.137,33.13,21.73,12.907,0.49,0.053,✔️,0.0,False,CPU
36
- 34,🧠⚡,Foundation Model,[TabSwift (default)](https://github.com/LAMDA-Tabular/TabSwift),1344.0,+118/-102,0.157,34.99,24.16,13.337,0.51,0.127,➖,0.0,False,GPU
37
- 35,🧠⚡,Foundation Model,[TabDPT (default)](https://arxiv.org/abs/2410.18164),1332.0,+119/-134,0.174,35.84,16.46,15.311,46.62,43.738,✔️,0.0,False,GPU
38
- 36,🧠🔁,Neural Network,[TabM (default)](https://arxiv.org/abs/2410.24210),1326.0,+110/-88,0.096,36.23,29.85,13.982,2.13,0.047,✔️,0.0,False,GPU
39
- 37,🌳,Tree-based,[EBM (tuned + ensembled)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1326.0,+113/-128,0.134,36.24,27.26,15.595,695.61,0.085,✔️,0.0,False,CPU
40
- 38,🧠🔁,Neural Network,[ModernNCA (default)](https://arxiv.org/abs/2407.03257),1298.0,+72/-79,0.073,38.1,33.85,14.33,8.06,0.25,✔️,0.0,False,GPU
41
- 39,🧠⚡,Foundation Model,[LimiX (default)](https://arxiv.org/abs/2509.03505),1279.0,+140/-160,0.17,39.33,9.92,16.418,6.55,5.409,➖,0.0,False,GPU
42
- 40,🧠🔁,Neural Network,[TorchMLP (tuned)](https://arxiv.org/abs/2003.06505),1272.0,+70/-75,0.049,39.78,35.01,14.114,1770.56,0.051,✔️,0.0,False,CPU
43
- 41,🧠🔁,Neural Network,[RealMLP (default)](https://arxiv.org/abs/2407.04491),1272.0,+73/-68,0.034,39.84,36.58,15.091,6.75,0.235,✔️,0.0,False,GPU
44
- 42,🌳,Tree-based,[EBM (tuned)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1265.0,+104/-133,0.089,40.24,32.83,16.441,695.61,0.006,✔️,0.0,False,CPU
45
- 43,🌳,Tree-based,[LightGBM (default)](https://papers.nips.cc/paper_files/paper/2017/hash/6449f44a102fde848669bdd9eb6b76fa-Abstract.html),1258.0,+51/-64,0.028,40.73,38.32,14.82,0.29,0.045,✔️,0.0,False,CPU
46
- 44,🌳,Tree-based,[RandomForest (tuned + ensembled)](https://link.springer.com/article/10.1023/A:1010933404324),1244.0,+85/-85,0.03,41.64,35.97,15.1,66.0,0.341,✔️,0.0,False,CPU
47
- 45,🌳,Tree-based,[EBM (default)](https://www.cs.cornell.edu/~yinlou/papers/lou-kdd13.pdf),1221.0,+88/-136,0.057,43.14,37.72,17.87,2.18,0.005,✔️,0.0,False,CPU
48
- 46,🌳,Tree-based,[ExtraTrees (tuned + ensembled)](https://link.springer.com/article/10.1007/s10994-006-6226-1),1217.0,+79/-89,0.022,43.4,40.85,15.304,49.61,0.199,✔️,0.0,False,CPU
49
- 47,🌳,Tree-based,[RandomForest (tuned)](https://link.springer.com/article/10.1023/A:1010933404324),1214.0,+86/-84,0.031,43.54,37.85,15.744,66.0,0.024,✔️,0.0,False,CPU
50
- 48,🧠🔁,Neural Network,[FastaiMLP (tuned + ensembled)](https://arxiv.org/abs/2003.06505),1201.0,+110/-138,0.06,44.39,36.49,17.192,351.94,1.658,✔️,0.0,False,CPU
51
- 49,🧠⚡,Foundation Model,[SAP-RPT-OSS (default)](https://arxiv.org/abs/2506.10707),1201.0,+82/-95,0.028,44.41,39.08,16.933,11.44,10.416,➖,0.0,False,GPU
52
- 50,🧠⚡,Foundation Model,[iLTM (default)](https://arxiv.org/abs/2511.15941),1179.0,+84/-111,0.033,45.73,41.33,18.488,88.27,9.62,✔️,0.0,False,GPU
53
- 51,🌳,Tree-based,[ExtraTrees (tuned)](https://link.springer.com/article/10.1007/s10994-006-6226-1),1174.0,+70/-83,0.008,46.07,43.76,16.303,49.61,0.015,✔️,0.0,False,CPU
54
- 52,🧠🔁,Neural Network,[TorchMLP (default)](https://arxiv.org/abs/2003.06505),1126.0,+59/-72,0.001,48.9,47.16,18.145,2.18,0.039,✔️,0.0,False,CPU
55
- 53,🌳,Tree-based,[PerpetualBooster (tuned + ensembled)](https://perpetual-ml.com/),1110.0,+101/-125,0.019,49.83,43.02,21.633,20.26,0.515,✔️,0.0,False,CPU
56
- 54,🧠🔁,Neural Network,[FastaiMLP (tuned)](https://arxiv.org/abs/2003.06505),1101.0,+91/-105,0.015,50.32,45.23,19.294,351.94,0.114,✔️,0.0,False,CPU
57
- 55,🌳,Tree-based,[PerpetualBooster (tuned)](https://perpetual-ml.com/),1060.0,+92/-116,0.004,52.57,48.1,22.747,20.26,0.2,✔️,0.0,False,CPU
58
- 56,❓,Other,[xRFM (default)](https://arxiv.org/abs/2508.10053),1046.0,+147/-184,0.018,53.27,47.13,25.98,2.49,0.145,✔️,0.0,False,GPU
59
- 57,🧠🔁,Neural Network,[FastaiMLP (default)](https://arxiv.org/abs/2003.06505),1031.0,+136/-171,0.018,54.04,48.76,22.864,1.51,0.111,✔️,0.0,False,CPU
60
- 58,🌳,Tree-based,[RandomForest (default)](https://link.springer.com/article/10.1023/A:1010933404324),1000.0,+51/-92,0.0,55.55,54.46,21.197,0.2,0.021,✔️,0.0,False,CPU
61
- 59,📏,Baseline,[KNN (tuned + ensembled)](https://scikit-learn.org/stable/modules/neighbors.html),989.0,+94/-128,0.005,56.06,49.75,24.353,150.3,10.071,✔️,0.0,False,CPU
62
- 60,🧠⚡,Foundation Model,[TabSTAR (tuned)](https://arxiv.org/abs/2505.18125),988.0,+56/-90,0.0,56.13,54.65,25.807,3381.77,1.724,✔️,0.0,False,GPU
63
- 61,🧠⚡,Foundation Model,[TabSTAR (tuned + ensembled)](https://arxiv.org/abs/2505.18125),987.0,+61/-86,0.0,56.18,54.79,25.781,3381.77,5.283,✔️,0.0,False,GPU
64
- 62,🌳,Tree-based,[ExtraTrees (default)](https://link.springer.com/article/10.1007/s10994-006-6226-1),976.0,+94/-121,0.0,56.64,54.71,22.789,0.48,0.073,✔️,0.0,False,CPU
65
- 63,📏,Baseline,[Linear (tuned + ensembled)](https://scikit-learn.org/stable/modules/linear_model.html),935.0,+153/-226,0.004,58.45,53.87,32.872,43.64,0.081,✔️,0.0,False,CPU
66
- 64,🌳,Tree-based,[PerpetualBooster (default)](https://perpetual-ml.com/),901.0,+106/-153,0.0,59.81,57.82,29.424,1.5,0.015,✔️,0.0,False,CPU
67
- 65,📏,Baseline,[Linear (tuned)](https://scikit-learn.org/stable/modules/linear_model.html),892.0,+154/-245,0.002,60.16,56.48,33.73,43.64,0.018,✔️,0.0,False,CPU
68
- 66,🧠⚡,Foundation Model,[TabSTAR (default)](https://arxiv.org/abs/2505.18125),848.0,+50/-88,0.0,61.7,61.11,30.685,110.78,1.964,✔️,0.0,False,GPU
69
- 67,📏,Baseline,[KNN (tuned)](https://scikit-learn.org/stable/modules/neighbors.html),814.0,+53/-106,0.0,62.78,62.5,29.973,150.3,0.482,✔️,0.0,False,CPU
70
- 68,📏,Baseline,[Linear (default)](https://scikit-learn.org/stable/modules/linear_model.html),797.0,+137/-244,0.0,63.27,61.49,35.794,0.19,0.019,✔️,0.0,False,CPU
71
- 69,📏,Baseline,[KNN (default)](https://scikit-learn.org/stable/modules/neighbors.html),593.0,+83/-146,0.0,67.65,67.52,43.501,0.1,0.072,✔️,0.0,False,CPU
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_explorer.html DELETED
@@ -1,1169 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Arena win-rate matrix</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* The matrix scrolls in both directions: 40+ models will not fit a page either
269
- way, and shrinking the cells to fit makes the numbers unreadable. `margin:
270
- auto` centres it once the selection is small enough to fit — and collapses
271
- to zero when it overflows, which `justify-content: center` would not. */
272
- .wr-scroll {
273
- overflow: auto; max-width: 100%;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .wr-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
277
- .wr-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- #chart { display: block; margin-inline: auto; }
282
- .wr-empty { padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
283
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
284
- .chips { margin: 2px 0 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
285
- .chiprow { flex: 0 1 auto; min-width: 0; }
286
- </style>
287
- </head>
288
- <body>
289
- <div class="viewbar">
290
- <button class="btn" id="btn-paper" title="White background, matrix only — for slides and papers">Paper view</button>
291
- </div>
292
- <p class="explorer-title" id="title"></p>
293
- <div class="controls">
294
- <button class="btn toggle" id="btn-best"
295
- title="Keep only each model's best-performing variant, one row and column per model — as the static figure does">One per model</button>
296
- <span class="grouplabel">Variants</span>
297
- <div class="btnrow" id="variant-btns"></div>
298
- <label class="metricpick">Order
299
- <select id="order-select">
300
- <option value="published">As published</option>
301
- <option value="winrate">Mean win rate</option>
302
- <option value="name">Name</option>
303
- </select>
304
- </label>
305
- <label class="metricpick">Size
306
- <select id="zoom-select" title="Fit scales the whole matrix into the panel; the percentages draw it at reading size and scroll">
307
- <option value="fit">Fit panel</option>
308
- <option value="0.6">60%</option>
309
- <option value="0.8">80%</option>
310
- <option value="1">100%</option>
311
- </select>
312
- </label>
313
- <span class="hint">Click a row or column label to remove that model</span>
314
- </div>
315
- <div class="exportbar" id="exportbar" hidden>
316
- <span class="hint">Export figure</span>
317
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">Download SVG</button>
318
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">Download PDF</button>
319
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">Download PNG</button>
320
- </div>
321
- <div class="chips" id="chips"></div>
322
- <div class="wr-scroll" id="wrap">
323
- <svg id="chart" role="img" aria-label="Pairwise win-rate matrix"></svg>
324
- </div>
325
- <div class="tooltip"></div>
326
-
327
- <script>
328
- (function () {
329
- "use strict";
330
- const CONFIG = {"title": null, "methods": ["TabFM (default)", "TabPFN-3 (default)", "TabPFN-2.6 (default)", "TabICLv2 (default)", "RealTabPFN-2.5 (T+E)", "RealMLP (T+E)", "ModernNCA (T+E)", "CatBoost (T+E)", "LightGBM (T+E)", "iLTM (T+E)", "XGBoost (T+E)", "TabM (T+E)", "TabDPT-Turbo (default)", "ChimeraBoost (T+E)", "TabDPT (T+E)", "xRFM (T+E)", "TorchMLP (T+E)", "TabSwift (default)", "EBM (T+E)", "LimiX (default)", "RandomForest (T+E)", "FastaiMLP (T+E)", "SAP-RPT-OSS (default)", "ExtraTrees (T+E)", "PerpetualBooster (T+E)", "TabSTAR (T+E)", "KNN (T+E)", "Linear (T+E)"], "matrix": [[NaN, 0.7851851582527161, 0.9333333373069763, 0.9037036895751953, 0.911111056804657, 0.8814814686775208, 0.9259259700775146, 0.8518518209457397, 0.9037036895751953, 0.8962963223457336, 0.9185184836387634, 0.9925926327705383, 0.9629629254341125, 0.9185184836387634, 0.8740740418434143, 0.9259259700775146, 0.9777777194976807, 0.970370352268219, 0.9925926327705383, 0.9037036895751953, 0.9851852059364319, 0.940740704536438, 0.9851852059364319, 0.9925926327705383, 1.0, 1.0, 0.970370352268219, 0.9851852059364319], [0.21481481194496155, NaN, 0.770370364189148, 0.6592592000961304, 0.82962965965271, 0.7629629969596863, 0.859259307384491, 0.7333333492279053, 0.7555555105209351, 0.7851851582527161, 0.7629629373550415, 0.8592592477798462, 0.9185184836387634, 0.7851851582527161, 0.7333333492279053, 0.800000011920929, 0.8888888955116272, 0.9777777791023254, 0.8444444537162781, 0.8666666746139526, 0.9629629850387573, 0.9333333373069763, 0.9481481909751892, 0.9777777791023254, 0.9333333373069763, 1.0, 0.9555556178092957, 0.9481481313705444], [0.06666667014360428, 0.22962965071201324, NaN, 0.4592593014240265, 0.555555522441864, 0.6888889074325562, 0.6740740537643433, 0.6518518328666687, 0.7333333492279053, 0.7037037014961243, 0.7185184955596924, 0.7333333492279053, 0.7185184359550476, 0.770370364189148, 0.7407407164573669, 0.8074074387550354, 0.8962963223457336, 0.9111111164093018, 0.9185184836387634, 0.8518518209457397, 0.970370352268219, 0.911111056804657, 0.970370352268219, 0.9851852059364319, 0.9925926327705383, 1.0, 0.9629629850387573, 0.9777777791023254], [0.09629630297422409, 0.34074074029922485, 0.5407407283782959, NaN, 0.7333333492279053, 0.644444465637207, 0.7407407164573669, 0.6740740537643433, 0.7111111283302307, 0.6888888478279114, 0.6814814209938049, 0.7407407164573669, 0.7851851582527161, 0.6814814805984497, 0.770370364189148, 0.7333333492279053, 0.8074073791503906, 0.940740704536438, 0.770370364189148, 0.8740740418434143, 0.9185184836387634, 0.8962963223457336, 0.9481481313705444, 0.9925926327705383, 0.9333333373069763, 0.9851852059364319, 0.970370352268219, 0.9481481313705444], [0.08888889104127884, 0.17037038505077362, 0.4444444477558136, 0.2666666805744171, NaN, 0.6000000238418579, 0.5777778029441833, 0.585185170173645, 0.6296296715736389, 0.614814817905426, 0.6592592597007751, 0.614814817905426, 0.6296296715736389, 0.6074073910713196, 0.6592592597007751, 0.7629629969596863, 0.8074073791503906, 0.9037037491798401, 0.8222221732139587, 0.7777777314186096, 0.9407407641410828, 0.8222222924232483, 0.9037037491798401, 0.9777777791023254, 0.9777777791023254, 1.0, 0.940740704536438, 0.8962962627410889], [0.11851851642131805, 0.2370370328426361, 0.3111111521720886, 0.35555559396743774, 0.4000000059604645, NaN, 0.5333333611488342, 0.5185185074806213, 0.6074074506759644, 0.6370370388031006, 0.6370370388031006, 0.5925925970077515, 0.5925925970077515, 0.5185185074806213, 0.5777778029441833, 0.859259307384491, 0.8370370864868164, 0.7555555701255798, 0.8222221732139587, 0.888888955116272, 0.9185184836387634, 0.9851852059364319, 0.9259259700775146, 0.9333333373069763, 0.9851852059364319, 1.0, 0.9925926327705383, 1.0], [0.07407407462596893, 0.14074073731899261, 0.32592591643333435, 0.25925925374031067, 0.42222222685813904, 0.46666666865348816, NaN, 0.5259259343147278, 0.5777778029441833, 0.6148148775100708, 0.6296296715736389, 0.6000000834465027, 0.5481481552124023, 0.6222222447395325, 0.6370370984077454, 0.7555555105209351, 0.8222222924232483, 0.7851851582527161, 0.8222222924232483, 0.8814814686775208, 0.859259307384491, 0.911111056804657, 0.9037036895751953, 0.9333333373069763, 0.9925926327705383, 0.9481481313705444, 0.9925926327705383, 0.9333333373069763], [0.14814814925193787, 0.2666666805744171, 0.3481481671333313, 0.32592594623565674, 0.414814829826355, 0.48148149251937866, 0.4740740656852722, NaN, 0.5185185074806213, 0.6000000238418579, 0.6888888478279114, 0.4888888895511627, 0.46666666865348816, 0.6962963342666626, 0.5333333611488342, 0.7185184955596924, 0.7925926446914673, 0.7481481432914734, 0.8962963223457336, 0.6814814805984497, 0.9259259700775146, 0.9111111164093018, 0.9185184836387634, 0.9703704118728638, 0.9777777791023254, 1.0, 0.9925926327705383, 0.9925926327705383], [0.09629630297422409, 0.24444444477558136, 0.2666666805744171, 0.28888893127441406, 0.37037041783332825, 0.39259257912635803, 0.42222222685813904, 0.48148149251937866, NaN, 0.5407407283782959, 0.6888889074325562, 0.4740740954875946, 0.5185185670852661, 0.644444465637207, 0.4962962865829468, 0.6444444060325623, 0.7555555105209351, 0.7259259819984436, 0.8444443941116333, 0.6592592597007751, 0.9333333373069763, 0.8444443941116333, 0.9037036895751953, 0.9851852655410767, 0.9851852059364319, 0.9925926327705383, 0.9851852059364319, 0.9851852059364319], [0.10370371490716934, 0.21481482684612274, 0.29629629850387573, 0.31111112236976624, 0.385185182094574, 0.3629629909992218, 0.38518521189689636, 0.40000003576278687, 0.4592592418193817, NaN, 0.5407407879829407, 0.4888888895511627, 0.43703702092170715, 0.5037037134170532, 0.5111111402511597, 0.6296296715736389, 0.8814815282821655, 0.703703761100769, 0.8000000715255737, 0.6888889074325562, 0.9111111164093018, 0.9703704118728638, 0.9111111164093018, 0.9333333373069763, 0.8666666746139526, 0.9925926327705383, 0.9851852655410767, 0.9851852655410767], [0.08148148655891418, 0.2370370477437973, 0.28148147463798523, 0.3185185492038727, 0.34074074029922485, 0.3629629909992218, 0.37037038803100586, 0.31111109256744385, 0.31111109256744385, 0.4592592120170593, NaN, 0.48888885974884033, 0.48148149251937866, 0.5333333611488342, 0.5259259343147278, 0.644444465637207, 0.8000000715255737, 0.6518518328666687, 0.8666666150093079, 0.6518518328666687, 0.9333333373069763, 0.8666666746139526, 0.9037037491798401, 0.9481481909751892, 0.9037037491798401, 0.9925926327705383, 0.9925926327705383, 1.0], [0.007407407276332378, 0.14074073731899261, 0.2666666805744171, 0.25925928354263306, 0.38518521189689636, 0.40740740299224854, 0.4000000059604645, 0.5111110806465149, 0.5259259343147278, 0.5111110806465149, 0.5111110806465149, NaN, 0.48148149251937866, 0.5185185074806213, 0.46666669845581055, 0.5629629492759705, 0.6666666865348816, 0.7259259819984436, 0.7111110687255859, 0.7111110687255859, 0.8222222924232483, 0.8814814686775208, 0.8666666746139526, 0.8888888955116272, 0.9185184836387634, 1.0, 0.9629629850387573, 0.9555556178092957], [0.03703703731298447, 0.08148147910833359, 0.28148147463798523, 0.21481482684612274, 0.37037035822868347, 0.40740740299224854, 0.45185184478759766, 0.5333333611488342, 0.48148149251937866, 0.5629629492759705, 0.5185185074806213, 0.5185185074806213, NaN, 0.5259259343147278, 0.46666669845581055, 0.6592592597007751, 0.7629629969596863, 0.8074073791503906, 0.6074073910713196, 0.7777777910232544, 0.7851852178573608, 0.7629629969596863, 0.7851851582527161, 0.8444444537162781, 0.9111111164093018, 0.9259259700775146, 0.9037036895751953, 0.8222222924232483], [0.08148147910833359, 0.21481482684612274, 0.22962962090969086, 0.3185185194015503, 0.39259254932403564, 0.48148149251937866, 0.3777777850627899, 0.3037037253379822, 0.35555556416511536, 0.4962962567806244, 0.46666666865348816, 0.48148149251937866, 0.4740740656852722, NaN, 0.5333333611488342, 0.7037037014961243, 0.6740740537643433, 0.6444444060325623, 0.8148148059844971, 0.6296296715736389, 0.859259307384491, 0.8370370268821716, 0.8518518209457397, 0.8370370864868164, 0.8444444537162781, 0.9333333373069763, 0.9629629850387573, 1.0], [0.1259259283542633, 0.2666666805744171, 0.25925925374031067, 0.22962962090969086, 0.34074077010154724, 0.42222222685813904, 0.3629629909992218, 0.46666666865348816, 0.5037037134170532, 0.4888888895511627, 0.4740740656852722, 0.5333333611488342, 0.5333333611488342, 0.46666666865348816, NaN, 0.7037037014961243, 0.7185184955596924, 0.7111110687255859, 0.6296296715736389, 0.7259259819984436, 0.7777777314186096, 0.7925925850868225, 0.82962965965271, 0.7851851582527161, 0.9851852655410767, 0.9037036895751953, 0.8962963223457336, 0.8370370268821716], [0.07407407462596893, 0.20000000298023224, 0.19259260594844818, 0.2666666507720947, 0.2370370477437973, 0.14074073731899261, 0.24444444477558136, 0.28148147463798523, 0.35555556416511536, 0.37037035822868347, 0.35555553436279297, 0.43703705072402954, 0.34074074029922485, 0.29629629850387573, 0.29629629850387573, NaN, 0.6000000238418579, 0.5111111402511597, 0.6296296715736389, 0.703703761100769, 0.7629629969596863, 0.8222221732139587, 0.7259259819984436, 0.8074074387550354, 0.8370370268821716, 0.9037036895751953, 0.9777777194976807, 0.9851852059364319], [0.02222222276031971, 0.111111119389534, 0.10370370745658875, 0.19259260594844818, 0.192592591047287, 0.16296297311782837, 0.17777778208255768, 0.2074074149131775, 0.24444444477558136, 0.11851852387189865, 0.20000000298023224, 0.3333333432674408, 0.2370370477437973, 0.32592594623565674, 0.28148147463798523, 0.4000000059604645, NaN, 0.5185185670852661, 0.5481481552124023, 0.6000000238418579, 0.7259259819984436, 0.7407407164573669, 0.8148148059844971, 0.7555555105209351, 0.7333332896232605, 0.9481481313705444, 0.940740704536438, 0.9407407641410828], [0.029629629105329514, 0.02222222276031971, 0.08888889104127884, 0.05925925821065903, 0.09629630297422409, 0.24444444477558136, 0.21481482684612274, 0.2518518567085266, 0.27407410740852356, 0.29629629850387573, 0.3481481671333313, 0.27407410740852356, 0.192592591047287, 0.35555553436279297, 0.2888889014720917, 0.4888888895511627, 0.48148149251937866, NaN, 0.5481481552124023, 0.5925925374031067, 0.5555555820465088, 0.7481482028961182, 0.6666666865348816, 0.6222221851348877, 0.7925926446914673, 0.8666666746139526, 0.8518518805503845, 0.8148148059844971], [0.007407407276332378, 0.15555556118488312, 0.08148147910833359, 0.22962965071201324, 0.17777778208255768, 0.17777778208255768, 0.17777778208255768, 0.10370370745658875, 0.15555556118488312, 0.20000001788139343, 0.13333334028720856, 0.2888889014720917, 0.3925926089286804, 0.18518519401550293, 0.37037038803100586, 0.37037038803100586, 0.45185184478759766, 0.45185187458992004, NaN, 0.46666666865348816, 0.6148148775100708, 0.6444444060325623, 0.6666666865348816, 0.6666666865348816, 0.6518518328666687, 0.9407407641410828, 0.8370370268821716, 0.9333333373069763], [0.0962962955236435, 0.13333334028720856, 0.14814814925193787, 0.1259259283542633, 0.222222238779068, 0.111111119389534, 0.11851852387189865, 0.3185185194015503, 0.34074074029922485, 0.31111109256744385, 0.3481481671333313, 0.2888889014720917, 0.222222238779068, 0.37037035822868347, 0.27407407760620117, 0.29629629850387573, 0.4000000059604645, 0.40740740299224854, 0.5333333611488342, NaN, 0.5111110806465149, 0.6222222447395325, 0.6000000238418579, 0.48148149251937866, 0.5629628896713257, 0.6888888478279114, 0.7111111283302307, 0.7629629969596863], [0.014814814552664757, 0.03703703731298447, 0.029629629105329514, 0.08148147910833359, 0.05925925821065903, 0.08148147910833359, 0.14074073731899261, 0.07407407462596893, 0.06666667014360428, 0.08888889104127884, 0.06666667014360428, 0.17777778208255768, 0.21481482684612274, 0.14074073731899261, 0.2222222238779068, 0.23703701794147491, 0.27407407760620117, 0.4444444477558136, 0.385185182094574, 0.4888888895511627, NaN, 0.5925925970077515, 0.6666667461395264, 0.555555522441864, 0.6518518328666687, 0.8740740418434143, 0.9259259700775146, 0.8666666746139526], [0.05925925821065903, 0.06666667014360428, 0.08888889104127884, 0.10370370745658875, 0.17777778208255768, 0.014814814552664757, 0.08888889104127884, 0.08888889104127884, 0.15555556118488312, 0.029629629105329514, 0.13333334028720856, 0.11851851642131805, 0.2370370328426361, 0.16296295821666718, 0.2074074149131775, 0.17777778208255768, 0.25925928354263306, 0.2518518567085266, 0.35555553436279297, 0.3777777850627899, 0.40740740299224854, NaN, 0.46666666865348816, 0.4592592418193817, 0.5703703761100769, 0.7185184955596924, 0.7185184955596924, 0.9259259700775146], [0.014814814552664757, 0.05185185372829437, 0.029629629105329514, 0.05185185372829437, 0.09629630297422409, 0.07407407462596893, 0.0962962955236435, 0.08148147910833359, 0.0962962955236435, 0.08888889104127884, 0.0962962955236435, 0.13333334028720856, 0.21481482684612274, 0.14814814925193787, 0.17037037014961243, 0.27407407760620117, 0.18518517911434174, 0.3333333432674408, 0.3333333432674408, 0.4000000059604645, 0.3333333730697632, 0.5333333611488342, NaN, 0.40740740299224854, 0.6814814805984497, 0.7259259819984436, 0.859259307384491, 0.8740740418434143], [0.007407407276332378, 0.02222222276031971, 0.014814814552664757, 0.007407407276332378, 0.02222222276031971, 0.06666667014360428, 0.06666667014360428, 0.029629629105329514, 0.014814814552664757, 0.06666667014360428, 0.05185185372829437, 0.111111119389534, 0.15555556118488312, 0.16296295821666718, 0.21481482684612274, 0.19259260594844818, 0.24444444477558136, 0.37777775526046753, 0.3333333432674408, 0.5185185074806213, 0.444444477558136, 0.5407407283782959, 0.5925925970077515, NaN, 0.6370370388031006, 0.8148148059844971, 0.82962965965271, 0.8148148059844971], [0.0, 0.06666667014360428, 0.007407407276332378, 0.06666667014360428, 0.02222222276031971, 0.014814814552664757, 0.007407407276332378, 0.02222222276031971, 0.014814814552664757, 0.13333334028720856, 0.09629630297422409, 0.08148147910833359, 0.08888889104127884, 0.15555556118488312, 0.014814814552664757, 0.16296297311782837, 0.2666666805744171, 0.2074074149131775, 0.3481481671333313, 0.43703705072402954, 0.3481481671333313, 0.4296296536922455, 0.3185185492038727, 0.3629629909992218, NaN, 0.5111111402511597, 0.6296296715736389, 0.7037037014961243], [0.0, 0.0, 0.0, 0.014814814552664757, 0.0, 0.0, 0.05185185372829437, 0.0, 0.007407407276332378, 0.007407407276332378, 0.007407407276332378, 0.0, 0.07407407462596893, 0.06666667014360428, 0.0962962955236435, 0.09629630297422409, 0.05185185372829437, 0.13333334028720856, 0.059259261935949326, 0.31111112236976624, 0.1259259283542633, 0.28148147463798523, 0.27407407760620117, 0.18518517911434174, 0.4888888895511627, NaN, 0.6074073910713196, 0.7111111283302307], [0.029629629105329514, 0.04444444552063942, 0.03703703731298447, 0.029629629105329514, 0.05925925821065903, 0.007407407276332378, 0.007407407276332378, 0.007407407276332378, 0.014814814552664757, 0.014814814552664757, 0.007407407276332378, 0.03703703731298447, 0.0962962955236435, 0.03703703731298447, 0.10370370745658875, 0.02222222276031971, 0.05925925821065903, 0.14814814925193787, 0.16296295821666718, 0.2888889014720917, 0.07407407462596893, 0.28148147463798523, 0.14074073731899261, 0.17037038505077362, 0.37037038803100586, 0.39259257912635803, NaN, 0.7185184955596924], [0.014814814552664757, 0.05185185372829437, 0.02222222276031971, 0.05185185372829437, 0.10370370745658875, 0.0, 0.06666667014360428, 0.007407407276332378, 0.014814814552664757, 0.014814814552664757, 0.0, 0.04444444552063942, 0.17777778208255768, 0.0, 0.16296295821666718, 0.014814814552664757, 0.05925925821065903, 0.18518517911434174, 0.06666667014360428, 0.2370370328426361, 0.13333334028720856, 0.07407407462596893, 0.1259259283542633, 0.18518519401550293, 0.29629629850387573, 0.2888888716697693, 0.28148147463798523, NaN]], "variants": ["Default", "Tuned", "Tuned + Ens."]};
331
- const POINTS = [{"method":"TabFM (default)","model":"TabFM","variant":"Default","family":"Foundation Model","mean":0.9366255094},{"method":"TabPFN-3 (default)","model":"TabPFN-3","variant":"Default","family":"Foundation Model","mean":0.8320987677},{"method":"TabPFN-2.6 (default)","model":"TabPFN-2.6","variant":"Default","family":"Foundation Model","mean":0.7629629592},{"method":"TabICLv2 (default)","model":"TabICLv2","variant":"Default","family":"Foundation Model","mean":0.7610425152},{"method":"RealTabPFN-2.5 (T+E)","model":"RealTabPFN-2.5 (T+E)","variant":"","family":"Foundation Model","mean":0.6919067279},{"method":"RealMLP (T+E)","model":"RealMLP (T+E)","variant":"","family":"Neural Network","mean":0.686694116},{"method":"ModernNCA (T+E)","model":"ModernNCA (T+E)","variant":"","family":"Neural Network","mean":0.6661179849},{"method":"CatBoost (T+E)","model":"CatBoost (T+E)","variant":"","family":"Tree-based","mean":0.6658436325},{"method":"LightGBM (T+E)","model":"LightGBM (T+E)","variant":"","family":"Tree-based","mean":0.6359396524},{"method":"iLTM (T+E)","model":"iLTM (T+E)","variant":"","family":"Foundation Model","mean":0.6170096273},{"method":"XGBoost (T+E)","model":"XGBoost (T+E)","variant":"","family":"Tree-based","mean":0.6021947966},{"method":"TabM (T+E)","model":"TabM (T+E)","variant":"","family":"Neural Network","mean":0.5950617325},{"method":"TabDPT-Turbo (default)","model":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","mean":0.5854595403},{"method":"ChimeraBoost (T+E)","model":"ChimeraBoost (T+E)","variant":"","family":"Tree-based","mean":0.5851851876},{"method":"TabDPT (T+E)","model":"TabDPT (T+E)","variant":"","family":"Foundation Model","mean":0.5840877984},{"method":"xRFM (T+E)","model":"xRFM (T+E)","variant":"","family":"Other","mean":0.4946502116},{"method":"TorchMLP (T+E)","model":"TorchMLP (T+E)","variant":"","family":"Neural Network","mean":0.4288065873},{"method":"TabSwift (default)","model":"TabSwift","variant":"Default","family":"Foundation Model","mean":0.409876551},{"method":"EBM (T+E)","model":"EBM (T+E)","variant":"","family":"Tree-based","mean":0.3901234651},{"method":"LimiX (default)","model":"LimiX","variant":"Default","family":"Foundation Model","mean":0.3706447209},{"method":"RandomForest (T+E)","model":"RandomForest (T+E)","variant":"","family":"Tree-based","mean":0.3133059004},{"method":"FastaiMLP (T+E)","model":"FastaiMLP (T+E)","variant":"","family":"Neural Network","mean":0.2748971204},{"method":"SAP-RPT-OSS (default)","model":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","mean":0.2735253841},{"method":"ExtraTrees (T+E)","model":"ExtraTrees (T+E)","variant":"","family":"Tree-based","mean":0.2724279859},{"method":"PerpetualBooster (T+E)","model":"PerpetualBooster (T+E)","variant":"","family":"Tree-based","mean":0.2043895844},{"method":"TabSTAR (T+E)","model":"TabSTAR (T+E)","variant":"","family":"Foundation Model","mean":0.1352537733},{"method":"KNN (T+E)","model":"KNN (T+E)","variant":"","family":"Baseline","mean":0.1245541837},{"method":"Linear (T+E)","model":"Linear (T+E)","variant":"","family":"Baseline","mean":0.0993141289}];
332
-
333
-
334
- const NS = "http://www.w3.org/2000/svg";
335
- // Baseline and Other are one bucket, as in the site's own type legend: they
336
- // already share a color, and each holds only a handful of methods.
337
- const FAM_MERGED = "Baseline / Other";
338
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
339
- // Normalized up front so every later lookup — colors, chips, sorting — sees
340
- // the merged family. Both templates declare POINTS above this block.
341
- for (const p of POINTS) p.family = famOf(p.family);
342
-
343
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
344
- const FAM_VAR = {
345
- "Foundation Model": "var(--fam-foundation)",
346
- "Tree-based": "var(--fam-tree)",
347
- "Neural Network": "var(--fam-nn)",
348
- "Reference Pipeline": "var(--fam-reference)",
349
- [FAM_MERGED]: "var(--fam-baseline)",
350
- };
351
- // The symbol the website shows for each family, so a family chip here reads the
352
- // same as the Type column on the site. Baseline and Other are one bucket, so
353
- // that chip carries both symbols.
354
- const FAM_SYMBOL = {
355
- "Foundation Model": "🧠⚡",
356
- "Tree-based": "🌳",
357
- "Neural Network": "🧠🔁",
358
- "Reference Pipeline": "📊",
359
- [FAM_MERGED]: "📏 ❓",
360
- };
361
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
362
- const VARIANT_VAR = {
363
- "Default": "var(--var-default)",
364
- "Tuned": "var(--var-tuned)",
365
- "Tuned + Ens.": "var(--var-tunedens)",
366
- };
367
-
368
- // A family chip's label: its symbol, its name and how many methods it holds.
369
- function famChipLabel(family, count) {
370
- const symbol = FAM_SYMBOL[family];
371
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
372
- family + ' <span class="count">&times;' + count + "</span>";
373
- }
374
-
375
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
376
- const FAM_INK = {
377
- "Foundation Model": "var(--fam-foundation-ink)",
378
- "Tree-based": "var(--fam-tree-ink)",
379
- "Neural Network": "var(--fam-nn-ink)",
380
- "Reference Pipeline": "var(--fam-reference-ink)",
381
- [FAM_MERGED]: "var(--fam-baseline-ink)",
382
- };
383
-
384
- // Create an SVG element with attributes, optionally appended to `parent`.
385
- function el(name, attrs, parent) {
386
- const node = document.createElementNS(NS, name);
387
- for (const k in attrs) node.setAttribute(k, attrs[k]);
388
- if (parent) parent.appendChild(node);
389
- return node;
390
- }
391
-
392
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
393
- // locale-independent by definition, which is the point: `toLocaleString`
394
- // would follow the *viewer's* browser locale and print 1234,5 for a German
395
- // visitor, disagreeing with the figures and CSVs beside it.
396
- function fmtNum(v, decimals) {
397
- if (v == null || !isFinite(v)) return "—";
398
- return v.toFixed(decimals);
399
- }
400
-
401
- function fmtMetric(metric, v) {
402
- if (v == null || !isFinite(v)) return "—";
403
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
404
- }
405
-
406
- function fmtTime(v) {
407
- if (v >= 100) return fmtNum(v, 0) + " s";
408
- if (v >= 1) return fmtNum(v, 1) + " s";
409
- if (v >= 0.1) return fmtNum(v, 2) + " s";
410
- return fmtNum(v, 3) + " s";
411
- }
412
-
413
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
414
- function niceStep(raw) {
415
- if (!(raw > 0)) return 1;
416
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
417
- for (const m of [1, 2, 2.5, 5, 10]) {
418
- if (mag * m >= raw) return mag * m;
419
- }
420
- return mag * 10;
421
- }
422
-
423
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
424
- // index rather than by accumulation so fractional steps do not drift.
425
- function ticks(min, max, target) {
426
- const step = niceStep((max - min) / target);
427
- const first = Math.ceil(min / step);
428
- const out = [];
429
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
430
- return out.length ? out : [min];
431
- }
432
-
433
- // A hover tooltip anchored inside `box` (which must be position:relative).
434
- function makeTooltip(box) {
435
- const node = box.querySelector(".tooltip");
436
- return {
437
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
438
- move(ev) {
439
- const r = box.getBoundingClientRect();
440
- let tx = ev.clientX - r.left + 14;
441
- const ty = ev.clientY - r.top + 12;
442
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
443
- node.style.left = tx + "px";
444
- node.style.top = ty + "px";
445
- },
446
- hide() { node.style.display = "none"; },
447
- };
448
- }
449
-
450
- // Paper view — white surface, legend + chart only — is the *default*: what a
451
- // reader wants first is the figure, and it is the state worth exporting. The
452
- // controls, chip list and data table are one click away behind "Edit view".
453
- // `afterToggle` re-renders charts whose size is measured from the layout.
454
- // `options.openInPaper` (default true) decides the state the page opens in. A
455
- // chart opens as the figure; the leaderboard table opens with its controls,
456
- // since there the interaction is the point rather than scaffolding around it.
457
- function setUpPaperView(afterToggle, options) {
458
- const opts = options || {};
459
- const root = document.documentElement;
460
- let hostTheme = null; // the embedding page's choice, captured on entry
461
- const btn = document.getElementById("btn-paper");
462
- const embedded = window.parent !== window;
463
-
464
- function setPaper(on) {
465
- document.body.classList.toggle("paper", on);
466
- if (on) {
467
- hostTheme = root.getAttribute("data-theme");
468
- root.setAttribute("data-theme", "light");
469
- } else if (hostTheme) {
470
- root.setAttribute("data-theme", hostTheme);
471
- } else {
472
- root.removeAttribute("data-theme");
473
- }
474
- btn.textContent = on ? "Edit view" : "Paper view";
475
- document.getElementById("exportbar").hidden = !on || embedded;
476
- if (afterToggle) requestAnimationFrame(afterToggle);
477
- postHeight();
478
- }
479
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
480
- // Embedded, the host page owns these controls — they sit beside the panel's
481
- // static-figure toggle and are driven from the outside. Standalone (the
482
- // shareable single file) this page needs its own.
483
- if (embedded) document.querySelector(".viewbar").hidden = true;
484
- window.addEventListener("message", ev => {
485
- const d = ev.data;
486
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
487
- });
488
- // Only standalone: embedded, the host owns the button and would not see the
489
- // key press, so its label would fall out of step with the frame.
490
- if (!embedded) {
491
- document.addEventListener("keydown", ev => {
492
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
493
- });
494
- }
495
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
496
- }
497
-
498
- // --- Figure export ---------------------------------------------------------
499
- // The chart is live SVG, so a file can be built from it directly. Three things
500
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
501
- // nothing outside this document), it has no background or font of its own, and
502
- // the legend is HTML rather than part of the SVG.
503
-
504
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
505
- // cloned and its label re-emitted at the measured position. foreignObject would
506
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
507
- // break the PNG path.
508
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
509
- // once the node leaves this document.
510
- function resolveVars(root, resolve) {
511
- for (const node of [root, ...root.querySelectorAll("*")]) {
512
- for (const attr of ["fill", "stroke"]) {
513
- const value = node.getAttribute(attr);
514
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
515
- }
516
- }
517
- }
518
-
519
- function legendToSvg(container, resolve) {
520
- const base = container.getBoundingClientRect();
521
- const group = document.createElementNS(NS, "g");
522
- let height = 0;
523
- for (const item of container.querySelectorAll(".item")) {
524
- const box = item.getBoundingClientRect();
525
- if (!box.width) continue;
526
- height = Math.max(height, box.bottom - base.top);
527
- let textLeft = box.left - base.left;
528
- const glyph = item.querySelector("svg");
529
- if (glyph) {
530
- const gbox = glyph.getBoundingClientRect();
531
- const wrap = el("g", {
532
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
533
- }, group);
534
- const glyphClone = glyph.cloneNode(true);
535
- resolveVars(glyphClone, resolve);
536
- wrap.appendChild(glyphClone);
537
- textLeft = gbox.right - base.left + 5;
538
- }
539
- const label = item.textContent.trim();
540
- if (!label) continue;
541
- const colored = item.querySelector("[style*='color']");
542
- const text = el("text", {
543
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
544
- fill: resolve(getComputedStyle(colored || item).color),
545
- }, group);
546
- text.textContent = label;
547
- }
548
- return { group, height: Math.ceil(height) };
549
- }
550
-
551
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
552
- // y-axis beside the scrolling plot) still exports as one figure.
553
- function buildExportSvg(parts, legendEl, pad = 10) {
554
- const rootStyle = getComputedStyle(document.documentElement);
555
- const resolve = value => String(value).replace(
556
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
557
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
558
-
559
- let chartW = 0, chartH = 0;
560
- for (const part of parts) {
561
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
562
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
563
- }
564
-
565
- const out = document.createElementNS(NS, "svg");
566
- out.setAttribute("xmlns", NS);
567
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
568
- let top = pad;
569
- const later = []; // built after the width is known
570
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
571
- if (legend && legend.height) {
572
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
573
- later.push(() => out.appendChild(legend.group));
574
- top += legend.height + 8;
575
- }
576
-
577
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
578
- const height = top + chartH + pad;
579
- out.setAttribute("width", Math.ceil(width));
580
- out.setAttribute("height", Math.ceil(height));
581
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
582
- for (const build of later) build();
583
-
584
- for (const part of parts) {
585
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
586
- const clone = part.svg.cloneNode(true);
587
- resolveVars(clone, resolve);
588
- while (clone.firstChild) group.appendChild(clone.firstChild);
589
- }
590
- return out;
591
- }
592
-
593
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
594
- function slugify(text) {
595
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
596
- }
597
-
598
- function downloadUrl(url, filename) {
599
- const a = document.createElement("a");
600
- a.href = url;
601
- a.download = filename;
602
- document.body.appendChild(a);
603
- a.click();
604
- a.remove();
605
- }
606
-
607
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
608
- function rasterize(svg, scale, done, fail) {
609
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
610
- const img = new Image();
611
- img.onload = () => {
612
- const canvas = document.createElement("canvas");
613
- canvas.width = Math.round(width * scale);
614
- canvas.height = Math.round(height * scale);
615
- const ctx = canvas.getContext("2d");
616
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
617
- ctx.drawImage(img, 0, 0);
618
- done(canvas, width, height);
619
- };
620
- img.onerror = fail;
621
- img.src = "data:image/svg+xml;charset=utf-8,"
622
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
623
- }
624
-
625
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
626
- // cost this page its zero-dependency, single-file property. The image is stored
627
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
628
- // in points to the figure's CSS size, so it prints at the size it appears here
629
- // and the pixels land at 96*scale dpi.
630
- async function buildPdf(canvas, cssWidth, cssHeight) {
631
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
632
- const rgb = new Uint8Array((pixels.length / 4) * 3);
633
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
634
- rgb[j] = pixels[i];
635
- rgb[j + 1] = pixels[i + 1];
636
- rgb[j + 2] = pixels[i + 2];
637
- }
638
- const deflated = new Uint8Array(await new Response(
639
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
640
-
641
- const encoder = new TextEncoder();
642
- const chunks = [];
643
- const offsets = [];
644
- let cursor = 0;
645
- const put = data => {
646
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
647
- chunks.push(bytes);
648
- cursor += bytes.length;
649
- };
650
- const object = (id, body, stream) => {
651
- offsets[id] = cursor;
652
- put(`${id} 0 obj\n${body}\n`);
653
- if (stream) {
654
- put("stream\n");
655
- put(stream);
656
- put("\nendstream\n");
657
- }
658
- put("endobj\n");
659
- };
660
-
661
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
662
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
663
- put("%PDF-1.4\n");
664
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
665
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
666
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
667
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
668
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
669
- object(4, "<< /Type /XObject /Subtype /Image "
670
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
671
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
672
- object(5, `<< /Length ${content.length} >>`, content);
673
-
674
- const xref = cursor;
675
- let table = "xref\n0 6\n0000000000 65535 f \n";
676
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
677
- put(table);
678
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
679
- return new Blob(chunks, { type: "application/pdf" });
680
- }
681
-
682
- // Wire up the export buttons; `getParts` is called per click so the file always
683
- // matches what is on screen. Returns a `run(format)` the host page can drive.
684
- function setUpExport(getParts, basename) {
685
- const buttons = {
686
- svg: document.getElementById("btn-svg"),
687
- png: document.getElementById("btn-png"),
688
- pdf: document.getElementById("btn-pdf"),
689
- };
690
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
691
-
692
- // A sandboxed frame has no modals, so a failure is reported on the button.
693
- function complain(format) {
694
- const button = buttons[format];
695
- if (!button) return;
696
- const label = button.textContent;
697
- button.textContent = "failed";
698
- setTimeout(() => { button.textContent = label; }, 2500);
699
- }
700
-
701
- function run(format) {
702
- const svg = figure();
703
- const name = basename();
704
- if (format === "svg") {
705
- downloadUrl("data:image/svg+xml;charset=utf-8,"
706
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
707
- return;
708
- }
709
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
710
- // points so the pixels already land near 200 dpi at print size.
711
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
712
- if (format === "png") {
713
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
714
- } else {
715
- buildPdf(canvas, cssWidth, cssHeight)
716
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
717
- .catch(() => complain("pdf"));
718
- }
719
- }, () => complain(format));
720
- }
721
-
722
- for (const format of Object.keys(buttons)) {
723
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
724
- }
725
- // Embedded, the buttons live in the host's panel header (see main.taExport).
726
- window.addEventListener("message", ev => {
727
- const d = ev.data;
728
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
729
- });
730
- }
731
-
732
- // When embedded, report the content height so the host page can size the
733
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
734
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
735
- // clamped to at least the iframe's current viewport, which turns the
736
- // resize round-trip into a grow-forever feedback loop. The change guard
737
- // stops re-posting once the height settles.
738
- let lastPostedHeight = 0;
739
- function postHeight() {
740
- if (window.parent === window) return;
741
- const height = Math.ceil(document.body.offsetHeight);
742
- if (Math.abs(height - lastPostedHeight) < 3) return;
743
- lastPostedHeight = height;
744
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
745
- }
746
-
747
-
748
- // POINTS is one record per matrix label (its model, variant, family and mean
749
- // win rate); the matrix itself is CONFIG.matrix, row-major over CONFIG.methods.
750
- const METHODS = CONFIG.methods;
751
- const MATRIX = CONFIG.matrix;
752
- const rowOf = new Map(METHODS.map((m, i) => [m, i]));
753
- const info = new Map(POINTS.map(p => [p.method, p]));
754
- const MODELS = [...new Set(POINTS.map(p => p.model))];
755
- const VARIANTS = CONFIG.variants.filter(v => POINTS.some(p => p.variant === v));
756
- const VARIANT_BTN = { "Tuned + Ens.": "Tuned + Ensembled" };
757
-
758
- // Reference pipelines are AutoML systems rather than models, so a first read of
759
- // the matrix should be model against model; they are one chip click away. Falls
760
- // back to everything if that would leave nothing to compare.
761
- const OFF_BY_DEFAULT = new Set(["Reference Pipeline"]);
762
- const familyOf = new Map(POINTS.map(p => [p.model, p.family]));
763
- const DEFAULT_MODELS = MODELS.filter(m => !OFF_BY_DEFAULT.has(familyOf.get(m)));
764
-
765
- const state = {
766
- models: new Set(DEFAULT_MODELS.length > 1 ? DEFAULT_MODELS : MODELS),
767
- variants: new Set(VARIANTS),
768
- // One entry per model by default, as the static figure shows it: 80 rows of
769
- // model-variant pairs is a wall, and the comparison people want first is
770
- // between models.
771
- best: true,
772
- order: "published",
773
- // Default to fitting the panel: at reading size the matrix is taller than a
774
- // screen, and the shape of the heatmap is what most readers want first.
775
- zoom: "fit",
776
- };
777
- // Ceiling on the fitted height. Fitting is normally width-driven; this only bites
778
- // on very tall selections, and it is set where the labels are still readable
779
- // rather than at half a screen, which would shrink 30 rows to 8px type.
780
- const FIT_H = 1040;
781
- // Nominal (drawn) size of the last render; the display size is a CSS scale of it.
782
- let nomW = 0, nomH = 0;
783
- const svg = document.getElementById("chart");
784
- const wrap = document.getElementById("wrap");
785
- const tooltip = document.querySelector(".tooltip");
786
- const chipsBox = document.getElementById("chips");
787
- const famChips = new Map();
788
- const chipByModel = new Map();
789
-
790
- // Sized against the static figure rather than the other explorers: that one uses
791
- // 16pt tick labels and 18pt axis labels on generously sized cells, and it stayed
792
- // the easier of the two to read. Weight still matches the explorers' 650, so a
793
- // model name looks like the same name everywhere.
794
- const LABEL_SIZE = 16, LABEL_WEIGHT = 650, CAPTION_SIZE = 18, CELL = 42, VALUE_SIZE = 15;
795
- // The colour key is a vertical bar to the right of the matrix, as on the static
796
- // figure. It sits beside the rows rather than above them, so it is in view
797
- // wherever the reader is in a tall matrix.
798
- const BAR_W = 30, BAR_GAP = 32, TICK_SIZE = 15;
799
- const SCALE_CAPTION = "win rate of the row over the column";
800
- const SCALE_CAPTION_SHORT = "win rate";
801
-
802
- // Purple (the column wins) through white to green (the row wins), the same
803
- // diverging reading as the static figure's PRGn colormap.
804
- const LOSE = [118, 42, 131], MID = [247, 247, 247], WIN = [27, 120, 55];
805
- function cellColor(rate) {
806
- if (rate == null || !isFinite(rate)) return "#8884";
807
- const t = Math.max(0, Math.min(1, rate));
808
- const [a, b, u] = t < 0.5 ? [LOSE, MID, t / 0.5] : [MID, WIN, (t - 0.5) / 0.5];
809
- return "rgb(" + a.map((v, i) => Math.round(v + u * (b[i] - v))).join(",") + ")";
810
- }
811
-
812
- // Measured where possible: the labels are model names of wildly different
813
- // lengths, and the margins have to clear the longest one exactly or a rotated
814
- // column label runs into the caption above it. `getComputedTextLength` returns 0
815
- // when the frame has not been laid out yet (it is lazily loaded, and may still be
816
- // off-screen on the first paint), which would collapse both margins and push
817
- // every label outside the viewBox — invisible until something forced a redraw.
818
- // So fall back to an estimate, and redraw once the fonts have settled.
819
- function textWidth(text, size, weight) {
820
- const probe = el("text", {
821
- "font-size": size || LABEL_SIZE, "font-weight": weight || LABEL_WEIGHT,
822
- }, svg);
823
- probe.textContent = text;
824
- let width = 0;
825
- try { width = probe.getComputedTextLength(); } catch (e) { width = 0; }
826
- probe.remove();
827
- return width > 0 ? width : text.length * (size || LABEL_SIZE) * 0.58;
828
- }
829
-
830
- // Labels always carry their variant tag — "(default)" / "(tuned + ensembled)" —
831
- // including in one-per-model mode, where the tag is precisely what says which
832
- // variant survived the filter. The static figure labels them the same way.
833
- function labelText(label) {
834
- return label;
835
- }
836
-
837
- function shown() {
838
- let list = METHODS.filter(label => {
839
- const p = info.get(label);
840
- if (!p || !state.models.has(p.model)) return false;
841
- return !p.variant || state.variants.has(p.variant);
842
- });
843
- if (state.best) {
844
- // Best *among the variants still selected*, so the variant toggles keep
845
- // meaning something in this mode.
846
- const pick = new Map();
847
- for (const label of list) {
848
- const p = info.get(label);
849
- const held = pick.get(p.model);
850
- if (!held || (info.get(held).mean || 0) < (p.mean || 0)) pick.set(p.model, label);
851
- }
852
- list = list.filter(label => pick.get(info.get(label).model) === label);
853
- }
854
- if (state.order === "winrate") {
855
- list.sort((a, b) => (info.get(b).mean || 0) - (info.get(a).mean || 0));
856
- } else if (state.order === "name") {
857
- list.sort((a, b) => labelText(a).localeCompare(labelText(b)));
858
- }
859
- return list;
860
- }
861
-
862
- function render() {
863
- const list = shown();
864
- svg.innerHTML = "";
865
- if (list.length < 2) {
866
- svg.setAttribute("width", 0);
867
- svg.setAttribute("height", 0);
868
- // Also drop the display scale, or the empty chart keeps the last figure's box.
869
- svg.style.width = svg.style.height = "";
870
- nomW = nomH = 0;
871
- wrap.querySelector(".wr-empty") ||
872
- wrap.insertAdjacentHTML("beforeend",
873
- '<p class="wr-empty">Select at least two models to compare.</p>');
874
- postHeight();
875
- return;
876
- }
877
- const empty = wrap.querySelector(".wr-empty");
878
- if (empty) empty.remove();
879
-
880
- const widths = list.map(label => textWidth(labelText(label)));
881
- const longest = Math.max(...widths);
882
- // A label rotated -60° rises 0.866 of its length; the captions sit in the
883
- // corner box rather than above the columns, where long names would cross them.
884
- // Floor the header so the two corner captions always have room, even when
885
- // every selected name is short.
886
- const headerH = Math.max(Math.ceil(longest * 0.866) + 14, 2 * CAPTION_SIZE + 26);
887
- const labelW = Math.ceil(longest) + 16;
888
- const n = list.length;
889
- // A -60° label anchored at (x, y) reaches (x + 0.5L, y - 0.866L): it grows up
890
- // *and right*. headerH covers the rise; this covers the run, or the rightmost
891
- // column's name is clipped by half its length.
892
- const overhang = Math.ceil(longest * 0.5);
893
- const matrixRight = labelW + n * CELL;
894
- // The key occupies the band to the right of the matrix; the label overhang is
895
- // above the matrix, so the two never collide and the wider of the two wins.
896
- const tickW = Math.ceil(textWidth("100%", TICK_SIZE, 700));
897
- const barX = matrixRight + BAR_GAP;
898
- const keyRight = barX + BAR_W + 12 + tickW + 10 + CAPTION_SIZE;
899
- const width = Math.max(matrixRight + overhang + 12, keyRight + 10);
900
- const height = headerH + n * CELL + 12;
901
- svg.setAttribute("width", width);
902
- svg.setAttribute("height", height);
903
- svg.setAttribute("viewBox", "0 0 " + width + " " + height);
904
-
905
- // Captions, stacked in the corner box and bold, as on the static figure.
906
- const capB = el("text", {
907
- x: 4, y: headerH - 34, "font-size": CAPTION_SIZE, "font-weight": 700, fill: "var(--ink)",
908
- }, svg);
909
- capB.textContent = "Model B: loser →";
910
- const capA = el("text", {
911
- x: 4, y: headerH - 14, "font-size": CAPTION_SIZE, "font-weight": 700, fill: "var(--ink)",
912
- }, svg);
913
- capA.textContent = "Model A: winner ↓";
914
-
915
- list.forEach((label, j) => {
916
- const x = labelW + j * CELL + CELL / 2 + 4;
917
- const text = el("text", {
918
- x: x, y: headerH - 8, "font-size": LABEL_SIZE, "font-weight": LABEL_WEIGHT,
919
- "text-anchor": "start", fill: FAM_INK[info.get(label).family] || "var(--ink)",
920
- transform: "rotate(-60 " + x + " " + (headerH - 8) + ")", cursor: "pointer",
921
- }, svg);
922
- text.textContent = labelText(label);
923
- text.addEventListener("click", () => toggleModel(info.get(label).model));
924
- });
925
-
926
- list.forEach((rowLabel, i) => {
927
- const y = headerH + i * CELL;
928
- const text = el("text", {
929
- x: labelW - 12, y: y + CELL / 2 + 6, "font-size": LABEL_SIZE, "font-weight": LABEL_WEIGHT,
930
- "text-anchor": "end", fill: FAM_INK[info.get(rowLabel).family] || "var(--ink)",
931
- cursor: "pointer",
932
- }, svg);
933
- text.textContent = labelText(rowLabel);
934
- text.addEventListener("click", () => toggleModel(info.get(rowLabel).model));
935
-
936
- list.forEach((colLabel, j) => {
937
- const rate = valueAt(rowLabel, colLabel);
938
- const x = labelW + j * CELL;
939
- const same = rowLabel === colLabel;
940
- const rect = el("rect", {
941
- x: x, y: y, width: CELL - 1, height: CELL - 1, rx: 2,
942
- fill: same ? "#8883" : cellColor(rate),
943
- }, svg);
944
- if (same) return;
945
- const value = el("text", {
946
- x: x + (CELL - 1) / 2, y: y + CELL / 2 + 5.5, "font-size": VALUE_SIZE,
947
- "font-weight": 600, "text-anchor": "middle",
948
- fill: Math.abs(rate - 0.5) > 0.3 ? "#ffffff" : "#14161a", "pointer-events": "none",
949
- }, svg);
950
- value.textContent = rate == null || !isFinite(rate) ? "" : Math.round(rate * 100);
951
- rect.addEventListener("mousemove", ev => showTip(ev, rowLabel, colLabel, rate));
952
- rect.addEventListener("mouseleave", () => { tooltip.style.display = "none"; });
953
- });
954
- });
955
-
956
- drawScale(barX, headerH, n * CELL - 2, tickW);
957
- nomW = width;
958
- nomH = height;
959
- applyZoom();
960
- }
961
-
962
- // Scale the figure for display only: the width/height *attributes* stay nominal,
963
- // so the export helpers keep building full-size figures, and the CSS box drives
964
- // what the page shows (the viewBox does the scaling, so it stays vector-sharp).
965
- function applyZoom() {
966
- if (!nomW || !nomH) return;
967
- let scale = Number(state.zoom);
968
- if (!(scale > 0)) {
969
- const availW = Math.max(240, (wrap.clientWidth || nomW) - 4);
970
- scale = Math.min(1, availW / nomW, FIT_H / nomH);
971
- }
972
- svg.style.width = Math.round(nomW * scale) + "px";
973
- svg.style.height = Math.round(nomH * scale) + "px";
974
- postHeight();
975
- }
976
-
977
- // The colour key, drawn into the figure so it is there in paper view and in an
978
- // exported SVG/PNG/PDF, not only beside the live chart.
979
- function drawScale(x, y, barH, tickW) {
980
- const id = "wr-ramp";
981
- const grad = el("linearGradient", { id: id, x1: "0", x2: "0", y1: "0", y2: "1" },
982
- el("defs", {}, svg));
983
- // Top of the bar is the row winning outright, the bottom is the column winning.
984
- for (const [offset, rate] of [[0, 1], [0.5, 0.5], [1, 0]]) {
985
- el("stop", { offset: offset, "stop-color": cellColor(rate) }, grad);
986
- }
987
- el("rect", {
988
- x: x, y: y, width: BAR_W, height: barH, rx: 7,
989
- fill: "url(#" + id + ")", stroke: "var(--muted)", "stroke-width": 1,
990
- }, svg);
991
- for (const [frac, label] of [[0, "100%"], [0.5, "50%"], [1, "0%"]]) {
992
- const ty = y + frac * barH;
993
- el("line", {
994
- x1: x + BAR_W, y1: ty, x2: x + BAR_W + 6, y2: ty,
995
- stroke: "var(--muted)", "stroke-width": 1.5,
996
- }, svg);
997
- const tick = el("text", {
998
- x: x + BAR_W + 11, y: ty + TICK_SIZE * 0.36, "font-size": TICK_SIZE,
999
- "font-weight": 700, "text-anchor": "start", fill: "var(--ink)",
1000
- }, svg);
1001
- tick.textContent = label;
1002
- }
1003
- // The label reads down the bar. It shortens rather than growing past the bar
1004
- // when only a couple of models are selected: anything above the bar would run
1005
- // into the rightmost column's rotated name.
1006
- const size = CAPTION_SIZE - 2;
1007
- const cx = x + BAR_W + 11 + tickW + 10 + size * 0.5;
1008
- const cy = y + barH / 2;
1009
- const caption = el("text", {
1010
- x: cx, y: cy, "font-size": size, "font-weight": 700,
1011
- "text-anchor": "middle", fill: "var(--ink)",
1012
- transform: "rotate(90 " + cx + " " + cy + ")",
1013
- }, svg);
1014
- caption.textContent =
1015
- textWidth(SCALE_CAPTION, size, 700) <= barH ? SCALE_CAPTION : SCALE_CAPTION_SHORT;
1016
- }
1017
-
1018
- function valueAt(rowLabel, colLabel) {
1019
- const i = rowOf.get(rowLabel), j = rowOf.get(colLabel);
1020
- if (i == null || j == null) return null;
1021
- const row = MATRIX[i];
1022
- return row ? row[j] : null;
1023
- }
1024
-
1025
- function showTip(ev, rowLabel, colLabel, rate) {
1026
- tooltip.style.display = "block";
1027
- tooltip.innerHTML =
1028
- '<div class="t-name">' + rowLabel + " vs " + colLabel + "</div>" +
1029
- "<div>" + rowLabel + " wins " + fmtNum(rate * 100, 1) + "% of tasks</div>" +
1030
- '<div class="t-var">' + colLabel + " wins " + fmtNum((1 - rate) * 100, 1) + "%</div>";
1031
- const box = document.body.getBoundingClientRect();
1032
- tooltip.style.left = (ev.clientX - box.left + 14) + "px";
1033
- tooltip.style.top = (ev.clientY - box.top + 12) + "px";
1034
- }
1035
-
1036
- // ---------- model chips, by family ----------
1037
- function familyModels(fam) {
1038
- return [...new Set(POINTS.filter(p => p.family === fam).map(p => p.model))];
1039
- }
1040
- function meanOfModel(model) {
1041
- const values = POINTS.filter(p => p.model === model && isFinite(p.mean)).map(p => p.mean);
1042
- return values.length ? Math.max(...values) : 0;
1043
- }
1044
- function buildChips() {
1045
- const head = document.createElement("div");
1046
- head.className = "chips-head";
1047
- head.textContent = "Models compared — click to remove, click a family to toggle the whole group";
1048
- chipsBox.appendChild(head);
1049
- for (const fam of FAM_ORDER) {
1050
- const members = familyModels(fam);
1051
- if (!members.length) continue;
1052
- members.sort((a, b) => meanOfModel(b) - meanOfModel(a));
1053
- const row = document.createElement("div");
1054
- row.className = "chiprow";
1055
- const famBtn = document.createElement("button");
1056
- famBtn.className = "famchip";
1057
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1058
- famBtn.innerHTML = famChipLabel(fam, members.length);
1059
- famBtn.title = "Toggle all " + members.length + " " + fam + " models";
1060
- famBtn.addEventListener("click", () => toggleFamily(fam));
1061
- row.appendChild(famBtn);
1062
- famChips.set(fam, famBtn);
1063
- const set = document.createElement("div");
1064
- set.className = "chipset";
1065
- for (const model of members) {
1066
- const b = document.createElement("button");
1067
- b.className = "chip";
1068
- b.style.setProperty("--fam", FAM_VAR[fam]);
1069
- b.innerHTML = '<span class="dot"></span><span></span>';
1070
- b.lastChild.textContent = model;
1071
- b.title = model + " — best mean win rate " + fmtNum(meanOfModel(model) * 100, 1) + "%";
1072
- b.addEventListener("click", () => toggleModel(model));
1073
- set.appendChild(b);
1074
- chipByModel.set(model, b);
1075
- }
1076
- row.appendChild(set);
1077
- chipsBox.appendChild(row);
1078
- }
1079
- }
1080
- function syncChips() {
1081
- for (const [model, b] of chipByModel) b.setAttribute("aria-pressed", String(state.models.has(model)));
1082
- for (const [fam, b] of famChips) {
1083
- b.setAttribute("aria-pressed", String(familyModels(fam).every(m => state.models.has(m))));
1084
- }
1085
- }
1086
- function toggleModel(model) {
1087
- if (state.models.has(model)) state.models.delete(model); else state.models.add(model);
1088
- syncChips();
1089
- render();
1090
- }
1091
- function toggleFamily(fam) {
1092
- const members = familyModels(fam);
1093
- const allOn = members.every(m => state.models.has(m));
1094
- for (const m of members) { if (allOn) state.models.delete(m); else state.models.add(m); }
1095
- syncChips();
1096
- render();
1097
- }
1098
-
1099
- // ---------- variant toggles ----------
1100
- const variantBtns = new Map();
1101
- function buildVariantBtns() {
1102
- const box = document.getElementById("variant-btns");
1103
- for (const v of VARIANTS) {
1104
- const b = document.createElement("button");
1105
- b.className = "btn toggle";
1106
- b.innerHTML = '<span class="swatch"></span>';
1107
- // Spelled out: the matrix labels carry the long form the data uses, so the
1108
- // toggle should not be the only place showing the abbreviated key.
1109
- b.appendChild(document.createTextNode(VARIANT_BTN[v] || v));
1110
- b.style.setProperty("--fam", VARIANT_VAR[v] || "var(--accent)");
1111
- b.title = "Show or hide the " + (VARIANT_BTN[v] || v).toLowerCase() + " results";
1112
- b.addEventListener("click", () => {
1113
- if (state.variants.has(v)) state.variants.delete(v); else state.variants.add(v);
1114
- syncVariantBtns();
1115
- render();
1116
- });
1117
- box.appendChild(b);
1118
- variantBtns.set(v, b);
1119
- }
1120
- }
1121
- function syncVariantBtns() {
1122
- for (const [v, b] of variantBtns) b.setAttribute("aria-pressed", String(state.variants.has(v)));
1123
- }
1124
-
1125
- // ---------- boot ----------
1126
- if (CONFIG.title) document.getElementById("title").textContent = CONFIG.title;
1127
- document.getElementById("zoom-select").addEventListener("change", ev => {
1128
- state.zoom = ev.target.value;
1129
- applyZoom();
1130
- });
1131
- window.addEventListener("resize", applyZoom);
1132
- // Embedded in a lazily shown panel, the first render can measure a zero-width
1133
- // panel; re-fit once it has a width. Width changes only, so the height applyZoom
1134
- // itself produces cannot feed back into another fit.
1135
- if (window.ResizeObserver) {
1136
- let lastWidth = 0;
1137
- new ResizeObserver(() => {
1138
- const w = wrap.clientWidth;
1139
- if (Math.abs(w - lastWidth) < 2) return;
1140
- lastWidth = w;
1141
- applyZoom();
1142
- }).observe(wrap);
1143
- }
1144
- document.getElementById("order-select").addEventListener("change", ev => {
1145
- state.order = ev.target.value;
1146
- render();
1147
- });
1148
- const bestBtn = document.getElementById("btn-best");
1149
- bestBtn.style.setProperty("--fam", "var(--accent)");
1150
- bestBtn.addEventListener("click", () => {
1151
- state.best = !state.best;
1152
- bestBtn.setAttribute("aria-pressed", String(state.best));
1153
- render();
1154
- });
1155
- bestBtn.setAttribute("aria-pressed", String(state.best));
1156
- setUpPaperView(render);
1157
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(CONFIG.title || document.title));
1158
- buildVariantBtns();
1159
- buildChips();
1160
- syncChips();
1161
- syncVariantBtns();
1162
- render();
1163
- // The first render may have had to estimate label widths (see textWidth); redraw
1164
- // with real measurements as soon as the fonts are in.
1165
- if (document.fonts && document.fonts.ready) document.fonts.ready.then(render);
1166
- })();
1167
- </script>
1168
- </body>
1169
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_matrix.csv DELETED
@@ -1,29 +0,0 @@
1
- ,TabFM (default),TabPFN-3 (default),TabPFN-2.6 (default),TabICLv2 (default),RealTabPFN-2.5 (T+E),RealMLP (T+E),ModernNCA (T+E),CatBoost (T+E),LightGBM (T+E),iLTM (T+E),XGBoost (T+E),TabM (T+E),TabDPT-Turbo (default),ChimeraBoost (T+E),TabDPT (T+E),xRFM (T+E),TorchMLP (T+E),TabSwift (default),EBM (T+E),LimiX (default),RandomForest (T+E),FastaiMLP (T+E),SAP-RPT-OSS (default),ExtraTrees (T+E),PerpetualBooster (T+E),TabSTAR (T+E),KNN (T+E),Linear (T+E)
2
- TabFM (default),,0.78518516,0.93333334,0.9037037,0.91111106,0.88148147,0.92592597,0.8518518,0.9037037,0.8962963,0.9185185,0.99259263,0.9629629,0.9185185,0.87407404,0.92592597,0.9777777,0.97037035,0.99259263,0.9037037,0.9851852,0.9407407,0.9851852,0.99259263,1.0,1.0,0.97037035,0.9851852
3
- TabPFN-3 (default),0.21481481,,0.77037036,0.6592592,0.82962966,0.762963,0.8592593,0.73333335,0.7555555,0.78518516,0.76296294,0.85925925,0.9185185,0.78518516,0.73333335,0.8,0.8888889,0.9777778,0.84444445,0.8666667,0.962963,0.93333334,0.9481482,0.9777778,0.93333334,1.0,0.9555556,0.94814813
4
- TabPFN-2.6 (default),0.06666667,0.22962965,,0.4592593,0.5555555,0.6888889,0.67407405,0.65185183,0.73333335,0.7037037,0.7185185,0.73333335,0.71851844,0.77037036,0.7407407,0.80740744,0.8962963,0.9111111,0.9185185,0.8518518,0.97037035,0.91111106,0.97037035,0.9851852,0.99259263,1.0,0.962963,0.9777778
5
- TabICLv2 (default),0.0962963,0.34074074,0.5407407,,0.73333335,0.64444447,0.7407407,0.67407405,0.7111111,0.68888885,0.6814814,0.7407407,0.78518516,0.6814815,0.77037036,0.73333335,0.8074074,0.9407407,0.77037036,0.87407404,0.9185185,0.8962963,0.94814813,0.99259263,0.93333334,0.9851852,0.97037035,0.94814813
6
- RealTabPFN-2.5 (T+E),0.08888889,0.17037039,0.44444445,0.26666668,,0.6,0.5777778,0.58518517,0.6296297,0.6148148,0.65925926,0.6148148,0.6296297,0.6074074,0.65925926,0.762963,0.8074074,0.90370375,0.8222222,0.77777773,0.94074076,0.8222223,0.90370375,0.9777778,0.9777778,1.0,0.9407407,0.89629626
7
- RealMLP (T+E),0.11851852,0.23703703,0.31111115,0.3555556,0.4,,0.53333336,0.5185185,0.60740745,0.63703704,0.63703704,0.5925926,0.5925926,0.5185185,0.5777778,0.8592593,0.8370371,0.75555557,0.8222222,0.88888896,0.9185185,0.9851852,0.92592597,0.93333334,0.9851852,1.0,0.99259263,1.0
8
- ModernNCA (T+E),0.074074075,0.14074074,0.32592592,0.25925925,0.42222223,0.46666667,,0.52592593,0.5777778,0.6148149,0.6296297,0.6000001,0.54814816,0.62222224,0.6370371,0.7555555,0.8222223,0.78518516,0.8222223,0.88148147,0.8592593,0.91111106,0.9037037,0.93333334,0.99259263,0.94814813,0.99259263,0.93333334
9
- CatBoost (T+E),0.14814815,0.26666668,0.34814817,0.32592595,0.41481483,0.4814815,0.47407407,,0.5185185,0.6,0.68888885,0.4888889,0.46666667,0.69629633,0.53333336,0.7185185,0.79259264,0.74814814,0.8962963,0.6814815,0.92592597,0.9111111,0.9185185,0.9703704,0.9777778,1.0,0.99259263,0.99259263
10
- LightGBM (T+E),0.0962963,0.24444444,0.26666668,0.28888893,0.37037042,0.39259258,0.42222223,0.4814815,,0.5407407,0.6888889,0.4740741,0.51851857,0.64444447,0.4962963,0.6444444,0.7555555,0.725926,0.8444444,0.65925926,0.93333334,0.8444444,0.9037037,0.98518527,0.9851852,0.99259263,0.9851852,0.9851852
11
- iLTM (T+E),0.103703715,0.21481483,0.2962963,0.31111112,0.38518518,0.362963,0.3851852,0.40000004,0.45925924,,0.5407408,0.4888889,0.43703702,0.5037037,0.51111114,0.6296297,0.8814815,0.70370376,0.8000001,0.6888889,0.9111111,0.9703704,0.9111111,0.93333334,0.8666667,0.99259263,0.98518527,0.98518527
12
- XGBoost (T+E),0.08148149,0.23703705,0.28148147,0.31851855,0.34074074,0.362963,0.3703704,0.3111111,0.3111111,0.4592592,,0.48888886,0.4814815,0.53333336,0.52592593,0.64444447,0.8000001,0.65185183,0.8666666,0.65185183,0.93333334,0.8666667,0.90370375,0.9481482,0.90370375,0.99259263,0.99259263,1.0
13
- TabM (T+E),0.0074074073,0.14074074,0.26666668,0.25925928,0.3851852,0.4074074,0.4,0.5111111,0.52592593,0.5111111,0.5111111,,0.4814815,0.5185185,0.4666667,0.56296295,0.6666667,0.725926,0.71111107,0.71111107,0.8222223,0.88148147,0.8666667,0.8888889,0.9185185,1.0,0.962963,0.9555556
14
- TabDPT-Turbo (default),0.037037037,0.08148148,0.28148147,0.21481483,0.37037036,0.4074074,0.45185184,0.53333336,0.4814815,0.56296295,0.5185185,0.5185185,,0.52592593,0.4666667,0.65925926,0.762963,0.8074074,0.6074074,0.7777778,0.7851852,0.762963,0.78518516,0.84444445,0.9111111,0.92592597,0.9037037,0.8222223
15
- ChimeraBoost (T+E),0.08148148,0.21481483,0.22962962,0.31851852,0.39259255,0.4814815,0.37777779,0.30370373,0.35555556,0.49629626,0.46666667,0.4814815,0.47407407,,0.53333336,0.7037037,0.67407405,0.6444444,0.8148148,0.6296297,0.8592593,0.837037,0.8518518,0.8370371,0.84444445,0.93333334,0.962963,1.0
16
- TabDPT (T+E),0.12592593,0.26666668,0.25925925,0.22962962,0.34074077,0.42222223,0.362963,0.46666667,0.5037037,0.4888889,0.47407407,0.53333336,0.53333336,0.46666667,,0.7037037,0.7185185,0.71111107,0.6296297,0.725926,0.77777773,0.7925926,0.82962966,0.78518516,0.98518527,0.9037037,0.8962963,0.837037
17
- xRFM (T+E),0.074074075,0.2,0.1925926,0.26666665,0.23703705,0.14074074,0.24444444,0.28148147,0.35555556,0.37037036,0.35555553,0.43703705,0.34074074,0.2962963,0.2962963,,0.6,0.51111114,0.6296297,0.70370376,0.762963,0.8222222,0.725926,0.80740744,0.837037,0.9037037,0.9777777,0.9851852
18
- TorchMLP (T+E),0.022222223,0.11111112,0.10370371,0.1925926,0.19259259,0.16296297,0.17777778,0.20740741,0.24444444,0.118518524,0.2,0.33333334,0.23703705,0.32592595,0.28148147,0.4,,0.51851857,0.54814816,0.6,0.725926,0.7407407,0.8148148,0.7555555,0.7333333,0.94814813,0.9407407,0.94074076
19
- TabSwift (default),0.02962963,0.022222223,0.08888889,0.05925926,0.0962963,0.24444444,0.21481483,0.25185186,0.2740741,0.2962963,0.34814817,0.2740741,0.19259259,0.35555553,0.2888889,0.4888889,0.4814815,,0.54814816,0.59259254,0.5555556,0.7481482,0.6666667,0.6222222,0.79259264,0.8666667,0.8518519,0.8148148
20
- EBM (T+E),0.0074074073,0.15555556,0.08148148,0.22962965,0.17777778,0.17777778,0.17777778,0.10370371,0.15555556,0.20000002,0.13333334,0.2888889,0.3925926,0.1851852,0.3703704,0.3703704,0.45185184,0.45185187,,0.46666667,0.6148149,0.6444444,0.6666667,0.6666667,0.65185183,0.94074076,0.837037,0.93333334
21
- LimiX (default),0.096296296,0.13333334,0.14814815,0.12592593,0.22222224,0.11111112,0.118518524,0.31851852,0.34074074,0.3111111,0.34814817,0.2888889,0.22222224,0.37037036,0.27407408,0.2962963,0.4,0.4074074,0.53333336,,0.5111111,0.62222224,0.6,0.4814815,0.5629629,0.68888885,0.7111111,0.762963
22
- RandomForest (T+E),0.014814815,0.037037037,0.02962963,0.08148148,0.05925926,0.08148148,0.14074074,0.074074075,0.06666667,0.08888889,0.06666667,0.17777778,0.21481483,0.14074074,0.22222222,0.23703702,0.27407408,0.44444445,0.38518518,0.4888889,,0.5925926,0.66666675,0.5555555,0.65185183,0.87407404,0.92592597,0.8666667
23
- FastaiMLP (T+E),0.05925926,0.06666667,0.08888889,0.10370371,0.17777778,0.014814815,0.08888889,0.08888889,0.15555556,0.02962963,0.13333334,0.11851852,0.23703703,0.16296296,0.20740741,0.17777778,0.25925928,0.25185186,0.35555553,0.37777779,0.4074074,,0.46666667,0.45925924,0.5703704,0.7185185,0.7185185,0.92592597
24
- SAP-RPT-OSS (default),0.014814815,0.051851854,0.02962963,0.051851854,0.0962963,0.074074075,0.096296296,0.08148148,0.096296296,0.08888889,0.096296296,0.13333334,0.21481483,0.14814815,0.17037037,0.27407408,0.18518518,0.33333334,0.33333334,0.4,0.33333337,0.53333336,,0.4074074,0.6814815,0.725926,0.8592593,0.87407404
25
- ExtraTrees (T+E),0.0074074073,0.022222223,0.014814815,0.0074074073,0.022222223,0.06666667,0.06666667,0.02962963,0.014814815,0.06666667,0.051851854,0.11111112,0.15555556,0.16296296,0.21481483,0.1925926,0.24444444,0.37777776,0.33333334,0.5185185,0.44444448,0.5407407,0.5925926,,0.63703704,0.8148148,0.82962966,0.8148148
26
- PerpetualBooster (T+E),0.0,0.06666667,0.0074074073,0.06666667,0.022222223,0.014814815,0.0074074073,0.022222223,0.014814815,0.13333334,0.0962963,0.08148148,0.08888889,0.15555556,0.014814815,0.16296297,0.26666668,0.20740741,0.34814817,0.43703705,0.34814817,0.42962965,0.31851855,0.362963,,0.51111114,0.6296297,0.7037037
27
- TabSTAR (T+E),0.0,0.0,0.0,0.014814815,0.0,0.0,0.051851854,0.0,0.0074074073,0.0074074073,0.0074074073,0.0,0.074074075,0.06666667,0.096296296,0.0962963,0.051851854,0.13333334,0.059259262,0.31111112,0.12592593,0.28148147,0.27407408,0.18518518,0.4888889,,0.6074074,0.7111111
28
- KNN (T+E),0.02962963,0.044444446,0.037037037,0.02962963,0.05925926,0.0074074073,0.0074074073,0.0074074073,0.014814815,0.014814815,0.0074074073,0.037037037,0.096296296,0.037037037,0.10370371,0.022222223,0.05925926,0.14814815,0.16296296,0.2888889,0.074074075,0.28148147,0.14074074,0.17037039,0.3703704,0.39259258,,0.7185185
29
- Linear (T+E),0.014814815,0.051851854,0.022222223,0.051851854,0.10370371,0.0,0.06666667,0.0074074073,0.014814815,0.014814815,0.0,0.044444446,0.17777778,0.0,0.16296296,0.014814815,0.05925926,0.18518518,0.06666667,0.23703703,0.13333334,0.074074075,0.12592593,0.1851852,0.2962963,0.28888887,0.28148147,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_medium/winrate_matrix.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:5107cfc6dd1a7b0b5399ca58f6b0a125daeec11b7c0b14110459409d99ffa3fa
3
- size 662271
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/leaderboard_overview_explorer.html DELETED
@@ -1,1411 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard explorer — no imputation | All Tasks | Small</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Chart: a fixed y-axis pane beside a horizontally scrolling plot pane, so
269
- the axis stays readable however far right the reader scrolls. */
270
- .lb-chartwrap { position: relative; display: flex; align-items: flex-start; }
271
- .lb-axis { flex: none; }
272
- .lb-scroll {
273
- flex: 1 1 auto; min-width: 0; overflow-x: auto; overflow-y: hidden;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lb-scroll::-webkit-scrollbar { height: 10px; }
277
- .lb-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- .rangepick input[type=range] { width: 96px; accent-color: var(--accent); cursor: pointer; }
282
- .rangeval { font-variant-numeric: tabular-nums; font-weight: 500; min-width: 3.4em; }
283
- .lb-empty { padding: 40px 0; text-align: center; color: var(--muted); font-size: 13px; }
284
- /* Puts the family key on its own line of the legend strip. */
285
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
286
- /* Family blocks pack side by side rather than stacking: below a full-width
287
- chart, one row per family leaves the small ones (Other, Baseline) each
288
- wasting a line. The wide families still claim a row of their own. */
289
- .chips { margin-top: 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
290
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
291
- .chiprow { flex: 0 1 auto; min-width: 0; }
292
- </style>
293
- </head>
294
- <body>
295
- <div class="viewbar">
296
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
297
- </div>
298
- <p class="explorer-title" id="title"></p>
299
- <!-- One control row above everything it scopes (axis, sorting, variants, selection). -->
300
- <div class="controls">
301
- <label class="metricpick">Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <label class="metricpick">Sort
305
- <select id="sort-select"></select>
306
- </label>
307
- <label class="metricpick rangepick" title="Raise the axis floor to spread out the top of the field">Zoom
308
- <input type="range" id="ymin-range" aria-label="Y-axis minimum">
309
- <span class="rangeval" id="ymin-val"></span>
310
- </label>
311
- <div class="btnrow" id="variant-btns"></div>
312
- <div class="btnrow">
313
- <button class="btn" id="btn-all">All methods</button>
314
- <button class="btn" id="btn-top">Top 15</button>
315
- <button class="btn" id="btn-none">Clear</button>
316
- </div>
317
- <span class="hint">Click a column or chip to remove a method &middot; hover for exact values</span>
318
- </div>
319
- <div class="exportbar" id="exportbar" hidden>
320
- <span class="hint">Export figure</span>
321
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
322
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
323
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
324
- </div>
325
- <div class="legendstrip" id="legendstrip"></div>
326
- <div class="lb-chartwrap" id="chartwrap">
327
- <svg id="axis" class="lb-axis" role="presentation"></svg>
328
- <div class="lb-scroll" id="scroller">
329
- <svg id="chart" role="img" aria-label="Leaderboard overview"></svg>
330
- </div>
331
- <div class="tooltip"></div>
332
- </div>
333
- <div class="chips" id="chips"></div>
334
- <details class="datatable">
335
- <summary>Data table</summary>
336
- <div class="tblwrap" id="tblwrap"></div>
337
- </details>
338
-
339
- <script>
340
- (function () {
341
- "use strict";
342
- const CONFIG = {"title": null, "metrics": [{"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": "", "ci": {"lo": "elo_lo", "hi": "elo_hi"}}, {"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "score", "label": "Score", "axisLabel": "Score \u2014 higher is better", "lowerBetter": false, "fromZero": true, "decimals": 3, "suffix": ""}, {"key": "rank", "label": "Average rank", "axisLabel": "Average rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}, {"key": "hrank", "label": "Harmonic rank", "axisLabel": "Harmonic rank \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 2, "suffix": ""}], "rankMetric": "elo"};
343
- const POINTS = [{"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","reference":false,"elo":1744.0,"imp":4.654,"score":0.897,"rank":6.9,"hrank":1.74,"elo_hi":1886.0,"elo_lo":1651.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1638.0,"imp":9.279,"score":0.642,"rank":10.67,"hrank":4.73,"elo_hi":1712.0,"elo_lo":1586.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","reference":false,"elo":1636.0,"imp":10.766,"score":0.675,"rank":10.77,"hrank":4.53,"elo_hi":1716.0,"elo_lo":1580.0,"imputed_pct":0.0,"imputed":false},{"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1599.0,"imp":11.961,"score":0.616,"rank":12.44,"hrank":6.63,"elo_hi":1668.0,"elo_lo":1552.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1595.0,"imp":11.198,"score":0.593,"rank":12.63,"hrank":5.6,"elo_hi":1681.0,"elo_lo":1533.0,"imputed_pct":0.0,"imputed":false},{"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","reference":false,"elo":1572.0,"imp":11.56,"score":0.633,"rank":13.78,"hrank":5.25,"elo_hi":1678.0,"elo_lo":1494.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1555.0,"imp":11.907,"score":0.547,"rank":14.67,"hrank":6.51,"elo_hi":1624.0,"elo_lo":1499.0,"imputed_pct":0.0,"imputed":false},{"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","reference":false,"elo":1542.0,"imp":12.32,"score":0.536,"rank":15.41,"hrank":8.41,"elo_hi":1622.0,"elo_lo":1493.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1480.0,"imp":14.533,"score":0.402,"rank":19.13,"hrank":11.38,"elo_hi":1542.0,"elo_lo":1436.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","reference":false,"elo":1448.0,"imp":15.33,"score":0.391,"rank":21.19,"hrank":9.11,"elo_hi":1522.0,"elo_lo":1394.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1448.0,"imp":15.246,"score":0.398,"rank":21.22,"hrank":9.27,"elo_hi":1522.0,"elo_lo":1388.0,"imputed_pct":0.0,"imputed":false},{"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":true,"elo":1444.0,"imp":15.598,"score":0.326,"rank":21.52,"hrank":13.45,"elo_hi":1495.0,"elo_lo":1398.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1429.0,"imp":15.675,"score":0.325,"rank":22.52,"hrank":12.22,"elo_hi":1481.0,"elo_lo":1381.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1415.0,"imp":15.884,"score":0.356,"rank":23.49,"hrank":10.85,"elo_hi":1485.0,"elo_lo":1357.0,"imputed_pct":0.0,"imputed":false},{"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","reference":false,"elo":1405.0,"imp":15.155,"score":0.353,"rank":24.21,"hrank":9.41,"elo_hi":1479.0,"elo_lo":1333.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1400.0,"imp":15.91,"score":0.293,"rank":24.6,"hrank":13.5,"elo_hi":1451.0,"elo_lo":1357.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1390.0,"imp":16.937,"score":0.235,"rank":25.34,"hrank":19.43,"elo_hi":1424.0,"elo_lo":1356.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1373.0,"imp":16.561,"score":0.258,"rank":26.55,"hrank":14.04,"elo_hi":1426.0,"elo_lo":1325.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1368.0,"imp":17.041,"score":0.211,"rank":26.99,"hrank":19.52,"elo_hi":1413.0,"elo_lo":1329.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","reference":false,"elo":1363.0,"imp":17.224,"score":0.29,"rank":27.32,"hrank":15.91,"elo_hi":1420.0,"elo_lo":1314.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1361.0,"imp":17.421,"score":0.199,"rank":27.46,"hrank":18.84,"elo_hi":1405.0,"elo_lo":1322.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1354.0,"imp":17.279,"score":0.192,"rank":28.01,"hrank":20.68,"elo_hi":1393.0,"elo_lo":1318.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1353.0,"imp":17.389,"score":0.185,"rank":28.07,"hrank":21.83,"elo_hi":1385.0,"elo_lo":1322.0,"imputed_pct":0.0,"imputed":false},{"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","reference":false,"elo":1332.0,"imp":18.023,"score":0.275,"rank":29.74,"hrank":13.6,"elo_hi":1406.0,"elo_lo":1265.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1330.0,"imp":17.868,"score":0.203,"rank":29.84,"hrank":18.17,"elo_hi":1378.0,"elo_lo":1288.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1325.0,"imp":17.924,"score":0.158,"rank":30.28,"hrank":23.33,"elo_hi":1358.0,"elo_lo":1288.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1323.0,"imp":18.056,"score":0.178,"rank":30.38,"hrank":15.63,"elo_hi":1363.0,"elo_lo":1281.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1323.0,"imp":18.319,"score":0.227,"rank":30.39,"hrank":13.02,"elo_hi":1384.0,"elo_lo":1267.0,"imputed_pct":0.0,"imputed":false},{"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","reference":false,"elo":1320.0,"imp":18.203,"score":0.159,"rank":30.6,"hrank":22.44,"elo_hi":1355.0,"elo_lo":1285.0,"imputed_pct":0.0,"imputed":false},{"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","reference":false,"elo":1319.0,"imp":18.839,"score":0.263,"rank":30.69,"hrank":11.14,"elo_hi":1390.0,"elo_lo":1262.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1306.0,"imp":18.133,"score":0.13,"rank":31.71,"hrank":23.26,"elo_hi":1338.0,"elo_lo":1266.0,"imputed_pct":0.0,"imputed":false},{"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","reference":false,"elo":1296.0,"imp":18.258,"score":0.188,"rank":32.54,"hrank":19.64,"elo_hi":1349.0,"elo_lo":1251.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1282.0,"imp":18.894,"score":0.157,"rank":33.6,"hrank":20.71,"elo_hi":1324.0,"elo_lo":1240.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1278.0,"imp":19.516,"score":0.133,"rank":33.92,"hrank":23.84,"elo_hi":1329.0,"elo_lo":1232.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1260.0,"imp":19.124,"score":0.125,"rank":35.33,"hrank":24.98,"elo_hi":1300.0,"elo_lo":1211.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1260.0,"imp":19.803,"score":0.11,"rank":35.37,"hrank":25.32,"elo_hi":1312.0,"elo_lo":1210.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1260.0,"imp":19.954,"score":0.109,"rank":35.38,"hrank":25.23,"elo_hi":1302.0,"elo_lo":1215.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1258.0,"imp":19.245,"score":0.092,"rank":35.55,"hrank":26.17,"elo_hi":1295.0,"elo_lo":1223.0,"imputed_pct":0.0,"imputed":false},{"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","reference":false,"elo":1237.0,"imp":21.221,"score":0.097,"rank":37.17,"hrank":21.05,"elo_hi":1284.0,"elo_lo":1186.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1231.0,"imp":20.511,"score":0.079,"rank":37.67,"hrank":26.59,"elo_hi":1279.0,"elo_lo":1187.0,"imputed_pct":0.0,"imputed":false},{"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","reference":false,"elo":1221.0,"imp":20.018,"score":0.079,"rank":38.44,"hrank":26.97,"elo_hi":1260.0,"elo_lo":1178.0,"imputed_pct":0.0,"imputed":false},{"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","reference":false,"elo":1216.0,"imp":20.941,"score":0.087,"rank":38.82,"hrank":25.36,"elo_hi":1268.0,"elo_lo":1158.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1214.0,"imp":20.712,"score":0.124,"rank":38.99,"hrank":22.53,"elo_hi":1272.0,"elo_lo":1154.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1211.0,"imp":21.641,"score":0.109,"rank":39.24,"hrank":24.93,"elo_hi":1280.0,"elo_lo":1148.0,"imputed_pct":0.0,"imputed":false},{"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","reference":false,"elo":1203.0,"imp":21.249,"score":0.074,"rank":39.86,"hrank":25.92,"elo_hi":1251.0,"elo_lo":1150.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1196.0,"imp":20.862,"score":0.078,"rank":40.43,"hrank":29.54,"elo_hi":1239.0,"elo_lo":1147.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1187.0,"imp":22.339,"score":0.101,"rank":41.1,"hrank":23.0,"elo_hi":1252.0,"elo_lo":1121.0,"imputed_pct":0.0,"imputed":false},{"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","reference":false,"elo":1166.0,"imp":21.229,"score":0.043,"rank":42.71,"hrank":35.31,"elo_hi":1209.0,"elo_lo":1122.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1165.0,"imp":21.847,"score":0.061,"rank":42.8,"hrank":27.62,"elo_hi":1216.0,"elo_lo":1113.0,"imputed_pct":0.0,"imputed":false},{"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","reference":false,"elo":1163.0,"imp":21.244,"score":0.018,"rank":42.91,"hrank":37.21,"elo_hi":1190.0,"elo_lo":1126.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1150.0,"imp":23.216,"score":0.073,"rank":43.96,"hrank":26.88,"elo_hi":1209.0,"elo_lo":1080.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":1132.0,"imp":25.491,"score":0.178,"rank":45.27,"hrank":12.31,"elo_hi":1209.0,"elo_lo":1049.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":1130.0,"imp":25.525,"score":0.174,"rank":45.38,"hrank":11.81,"elo_hi":1208.0,"elo_lo":1046.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1113.0,"imp":23.948,"score":0.046,"rank":46.61,"hrank":25.91,"elo_hi":1165.0,"elo_lo":1045.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1084.0,"imp":28.451,"score":0.026,"rank":48.72,"hrank":37.31,"elo_hi":1130.0,"elo_lo":1025.0,"imputed_pct":0.0,"imputed":false},{"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","reference":false,"elo":1066.0,"imp":24.914,"score":0.039,"rank":49.88,"hrank":31.58,"elo_hi":1128.0,"elo_lo":992.0,"imputed_pct":0.0,"imputed":false},{"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1058.0,"imp":24.761,"score":0.015,"rank":50.45,"hrank":36.89,"elo_hi":1104.0,"elo_lo":1008.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":1048.0,"imp":29.908,"score":0.014,"rank":51.08,"hrank":37.33,"elo_hi":1098.0,"elo_lo":991.0,"imputed_pct":0.0,"imputed":false},{"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","reference":false,"elo":1043.0,"imp":26.639,"score":0.034,"rank":51.4,"hrank":42.96,"elo_hi":1104.0,"elo_lo":957.0,"imputed_pct":0.0,"imputed":false},{"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","reference":false,"elo":1033.0,"imp":30.36,"score":0.109,"rank":52.04,"hrank":19.43,"elo_hi":1112.0,"elo_lo":909.0,"imputed_pct":0.0,"imputed":false},{"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","reference":false,"elo":1020.0,"imp":27.588,"score":0.017,"rank":52.89,"hrank":44.87,"elo_hi":1080.0,"elo_lo":951.0,"imputed_pct":0.0,"imputed":false},{"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","reference":false,"elo":1000.0,"imp":28.377,"score":0.004,"rank":54.07,"hrank":48.1,"elo_hi":1058.0,"elo_lo":929.0,"imputed_pct":0.0,"imputed":false},{"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","reference":false,"elo":1000.0,"imp":26.856,"score":0.005,"rank":54.08,"hrank":37.17,"elo_hi":1049.0,"elo_lo":938.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":1000.0,"imp":29.267,"score":0.022,"rank":54.08,"hrank":35.68,"elo_hi":1065.0,"elo_lo":906.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":974.0,"imp":33.999,"score":0.042,"rank":55.53,"hrank":37.13,"elo_hi":1060.0,"elo_lo":879.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":954.0,"imp":34.411,"score":0.03,"rank":56.63,"hrank":38.68,"elo_hi":1039.0,"elo_lo":855.0,"imputed_pct":0.0,"imputed":false},{"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","reference":false,"elo":947.0,"imp":32.717,"score":0.0,"rank":57.02,"hrank":41.72,"elo_hi":988.0,"elo_lo":889.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":912.0,"imp":33.953,"score":0.012,"rank":58.72,"hrank":42.52,"elo_hi":980.0,"elo_lo":823.0,"imputed_pct":0.0,"imputed":false},{"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","reference":false,"elo":879.0,"imp":37.25,"score":0.019,"rank":60.23,"hrank":43.4,"elo_hi":974.0,"elo_lo":761.0,"imputed_pct":0.0,"imputed":false},{"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","reference":false,"elo":670.0,"imp":47.022,"score":0.0,"rank":66.58,"hrank":57.89,"elo_hi":768.0,"elo_lo":536.0,"imputed_pct":0.0,"imputed":false}];
344
-
345
-
346
- const NS = "http://www.w3.org/2000/svg";
347
- // Baseline and Other are one bucket, as in the site's own type legend: they
348
- // already share a color, and each holds only a handful of methods.
349
- const FAM_MERGED = "Baseline / Other";
350
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
351
- // Normalized up front so every later lookup — colors, chips, sorting — sees
352
- // the merged family. Both templates declare POINTS above this block.
353
- for (const p of POINTS) p.family = famOf(p.family);
354
-
355
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
356
- const FAM_VAR = {
357
- "Foundation Model": "var(--fam-foundation)",
358
- "Tree-based": "var(--fam-tree)",
359
- "Neural Network": "var(--fam-nn)",
360
- "Reference Pipeline": "var(--fam-reference)",
361
- [FAM_MERGED]: "var(--fam-baseline)",
362
- };
363
- // The symbol the website shows for each family, so a family chip here reads the
364
- // same as the Type column on the site. Baseline and Other are one bucket, so
365
- // that chip carries both symbols.
366
- const FAM_SYMBOL = {
367
- "Foundation Model": "🧠⚡",
368
- "Tree-based": "🌳",
369
- "Neural Network": "🧠🔁",
370
- "Reference Pipeline": "📊",
371
- [FAM_MERGED]: "📏 ❓",
372
- };
373
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
374
- const VARIANT_VAR = {
375
- "Default": "var(--var-default)",
376
- "Tuned": "var(--var-tuned)",
377
- "Tuned + Ens.": "var(--var-tunedens)",
378
- };
379
-
380
- // A family chip's label: its symbol, its name and how many methods it holds.
381
- function famChipLabel(family, count) {
382
- const symbol = FAM_SYMBOL[family];
383
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
384
- family + ' <span class="count">&times;' + count + "</span>";
385
- }
386
-
387
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
388
- const FAM_INK = {
389
- "Foundation Model": "var(--fam-foundation-ink)",
390
- "Tree-based": "var(--fam-tree-ink)",
391
- "Neural Network": "var(--fam-nn-ink)",
392
- "Reference Pipeline": "var(--fam-reference-ink)",
393
- [FAM_MERGED]: "var(--fam-baseline-ink)",
394
- };
395
-
396
- // Create an SVG element with attributes, optionally appended to `parent`.
397
- function el(name, attrs, parent) {
398
- const node = document.createElementNS(NS, name);
399
- for (const k in attrs) node.setAttribute(k, attrs[k]);
400
- if (parent) parent.appendChild(node);
401
- return node;
402
- }
403
-
404
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
405
- // locale-independent by definition, which is the point: `toLocaleString`
406
- // would follow the *viewer's* browser locale and print 1234,5 for a German
407
- // visitor, disagreeing with the figures and CSVs beside it.
408
- function fmtNum(v, decimals) {
409
- if (v == null || !isFinite(v)) return "—";
410
- return v.toFixed(decimals);
411
- }
412
-
413
- function fmtMetric(metric, v) {
414
- if (v == null || !isFinite(v)) return "—";
415
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
416
- }
417
-
418
- function fmtTime(v) {
419
- if (v >= 100) return fmtNum(v, 0) + " s";
420
- if (v >= 1) return fmtNum(v, 1) + " s";
421
- if (v >= 0.1) return fmtNum(v, 2) + " s";
422
- return fmtNum(v, 3) + " s";
423
- }
424
-
425
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
426
- function niceStep(raw) {
427
- if (!(raw > 0)) return 1;
428
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
429
- for (const m of [1, 2, 2.5, 5, 10]) {
430
- if (mag * m >= raw) return mag * m;
431
- }
432
- return mag * 10;
433
- }
434
-
435
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
436
- // index rather than by accumulation so fractional steps do not drift.
437
- function ticks(min, max, target) {
438
- const step = niceStep((max - min) / target);
439
- const first = Math.ceil(min / step);
440
- const out = [];
441
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
442
- return out.length ? out : [min];
443
- }
444
-
445
- // A hover tooltip anchored inside `box` (which must be position:relative).
446
- function makeTooltip(box) {
447
- const node = box.querySelector(".tooltip");
448
- return {
449
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
450
- move(ev) {
451
- const r = box.getBoundingClientRect();
452
- let tx = ev.clientX - r.left + 14;
453
- const ty = ev.clientY - r.top + 12;
454
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
455
- node.style.left = tx + "px";
456
- node.style.top = ty + "px";
457
- },
458
- hide() { node.style.display = "none"; },
459
- };
460
- }
461
-
462
- // Paper view — white surface, legend + chart only — is the *default*: what a
463
- // reader wants first is the figure, and it is the state worth exporting. The
464
- // controls, chip list and data table are one click away behind "Edit view".
465
- // `afterToggle` re-renders charts whose size is measured from the layout.
466
- // `options.openInPaper` (default true) decides the state the page opens in. A
467
- // chart opens as the figure; the leaderboard table opens with its controls,
468
- // since there the interaction is the point rather than scaffolding around it.
469
- function setUpPaperView(afterToggle, options) {
470
- const opts = options || {};
471
- const root = document.documentElement;
472
- let hostTheme = null; // the embedding page's choice, captured on entry
473
- const btn = document.getElementById("btn-paper");
474
- const embedded = window.parent !== window;
475
-
476
- function setPaper(on) {
477
- document.body.classList.toggle("paper", on);
478
- if (on) {
479
- hostTheme = root.getAttribute("data-theme");
480
- root.setAttribute("data-theme", "light");
481
- } else if (hostTheme) {
482
- root.setAttribute("data-theme", hostTheme);
483
- } else {
484
- root.removeAttribute("data-theme");
485
- }
486
- btn.textContent = on ? "Edit view" : "Paper view";
487
- document.getElementById("exportbar").hidden = !on || embedded;
488
- if (afterToggle) requestAnimationFrame(afterToggle);
489
- postHeight();
490
- }
491
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
492
- // Embedded, the host page owns these controls — they sit beside the panel's
493
- // static-figure toggle and are driven from the outside. Standalone (the
494
- // shareable single file) this page needs its own.
495
- if (embedded) document.querySelector(".viewbar").hidden = true;
496
- window.addEventListener("message", ev => {
497
- const d = ev.data;
498
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
499
- });
500
- // Only standalone: embedded, the host owns the button and would not see the
501
- // key press, so its label would fall out of step with the frame.
502
- if (!embedded) {
503
- document.addEventListener("keydown", ev => {
504
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
505
- });
506
- }
507
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
508
- }
509
-
510
- // --- Figure export ---------------------------------------------------------
511
- // The chart is live SVG, so a file can be built from it directly. Three things
512
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
513
- // nothing outside this document), it has no background or font of its own, and
514
- // the legend is HTML rather than part of the SVG.
515
-
516
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
517
- // cloned and its label re-emitted at the measured position. foreignObject would
518
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
519
- // break the PNG path.
520
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
521
- // once the node leaves this document.
522
- function resolveVars(root, resolve) {
523
- for (const node of [root, ...root.querySelectorAll("*")]) {
524
- for (const attr of ["fill", "stroke"]) {
525
- const value = node.getAttribute(attr);
526
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
527
- }
528
- }
529
- }
530
-
531
- function legendToSvg(container, resolve) {
532
- const base = container.getBoundingClientRect();
533
- const group = document.createElementNS(NS, "g");
534
- let height = 0;
535
- for (const item of container.querySelectorAll(".item")) {
536
- const box = item.getBoundingClientRect();
537
- if (!box.width) continue;
538
- height = Math.max(height, box.bottom - base.top);
539
- let textLeft = box.left - base.left;
540
- const glyph = item.querySelector("svg");
541
- if (glyph) {
542
- const gbox = glyph.getBoundingClientRect();
543
- const wrap = el("g", {
544
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
545
- }, group);
546
- const glyphClone = glyph.cloneNode(true);
547
- resolveVars(glyphClone, resolve);
548
- wrap.appendChild(glyphClone);
549
- textLeft = gbox.right - base.left + 5;
550
- }
551
- const label = item.textContent.trim();
552
- if (!label) continue;
553
- const colored = item.querySelector("[style*='color']");
554
- const text = el("text", {
555
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
556
- fill: resolve(getComputedStyle(colored || item).color),
557
- }, group);
558
- text.textContent = label;
559
- }
560
- return { group, height: Math.ceil(height) };
561
- }
562
-
563
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
564
- // y-axis beside the scrolling plot) still exports as one figure.
565
- function buildExportSvg(parts, legendEl, pad = 10) {
566
- const rootStyle = getComputedStyle(document.documentElement);
567
- const resolve = value => String(value).replace(
568
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
569
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
570
-
571
- let chartW = 0, chartH = 0;
572
- for (const part of parts) {
573
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
574
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
575
- }
576
-
577
- const out = document.createElementNS(NS, "svg");
578
- out.setAttribute("xmlns", NS);
579
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
580
- let top = pad;
581
- const later = []; // built after the width is known
582
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
583
- if (legend && legend.height) {
584
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
585
- later.push(() => out.appendChild(legend.group));
586
- top += legend.height + 8;
587
- }
588
-
589
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
590
- const height = top + chartH + pad;
591
- out.setAttribute("width", Math.ceil(width));
592
- out.setAttribute("height", Math.ceil(height));
593
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
594
- for (const build of later) build();
595
-
596
- for (const part of parts) {
597
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
598
- const clone = part.svg.cloneNode(true);
599
- resolveVars(clone, resolve);
600
- while (clone.firstChild) group.appendChild(clone.firstChild);
601
- }
602
- return out;
603
- }
604
-
605
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
606
- function slugify(text) {
607
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
608
- }
609
-
610
- function downloadUrl(url, filename) {
611
- const a = document.createElement("a");
612
- a.href = url;
613
- a.download = filename;
614
- document.body.appendChild(a);
615
- a.click();
616
- a.remove();
617
- }
618
-
619
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
620
- function rasterize(svg, scale, done, fail) {
621
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
622
- const img = new Image();
623
- img.onload = () => {
624
- const canvas = document.createElement("canvas");
625
- canvas.width = Math.round(width * scale);
626
- canvas.height = Math.round(height * scale);
627
- const ctx = canvas.getContext("2d");
628
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
629
- ctx.drawImage(img, 0, 0);
630
- done(canvas, width, height);
631
- };
632
- img.onerror = fail;
633
- img.src = "data:image/svg+xml;charset=utf-8,"
634
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
635
- }
636
-
637
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
638
- // cost this page its zero-dependency, single-file property. The image is stored
639
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
640
- // in points to the figure's CSS size, so it prints at the size it appears here
641
- // and the pixels land at 96*scale dpi.
642
- async function buildPdf(canvas, cssWidth, cssHeight) {
643
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
644
- const rgb = new Uint8Array((pixels.length / 4) * 3);
645
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
646
- rgb[j] = pixels[i];
647
- rgb[j + 1] = pixels[i + 1];
648
- rgb[j + 2] = pixels[i + 2];
649
- }
650
- const deflated = new Uint8Array(await new Response(
651
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
652
-
653
- const encoder = new TextEncoder();
654
- const chunks = [];
655
- const offsets = [];
656
- let cursor = 0;
657
- const put = data => {
658
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
659
- chunks.push(bytes);
660
- cursor += bytes.length;
661
- };
662
- const object = (id, body, stream) => {
663
- offsets[id] = cursor;
664
- put(`${id} 0 obj\n${body}\n`);
665
- if (stream) {
666
- put("stream\n");
667
- put(stream);
668
- put("\nendstream\n");
669
- }
670
- put("endobj\n");
671
- };
672
-
673
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
674
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
675
- put("%PDF-1.4\n");
676
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
677
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
678
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
679
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
680
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
681
- object(4, "<< /Type /XObject /Subtype /Image "
682
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
683
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
684
- object(5, `<< /Length ${content.length} >>`, content);
685
-
686
- const xref = cursor;
687
- let table = "xref\n0 6\n0000000000 65535 f \n";
688
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
689
- put(table);
690
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
691
- return new Blob(chunks, { type: "application/pdf" });
692
- }
693
-
694
- // Wire up the export buttons; `getParts` is called per click so the file always
695
- // matches what is on screen. Returns a `run(format)` the host page can drive.
696
- function setUpExport(getParts, basename) {
697
- const buttons = {
698
- svg: document.getElementById("btn-svg"),
699
- png: document.getElementById("btn-png"),
700
- pdf: document.getElementById("btn-pdf"),
701
- };
702
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
703
-
704
- // A sandboxed frame has no modals, so a failure is reported on the button.
705
- function complain(format) {
706
- const button = buttons[format];
707
- if (!button) return;
708
- const label = button.textContent;
709
- button.textContent = "failed";
710
- setTimeout(() => { button.textContent = label; }, 2500);
711
- }
712
-
713
- function run(format) {
714
- const svg = figure();
715
- const name = basename();
716
- if (format === "svg") {
717
- downloadUrl("data:image/svg+xml;charset=utf-8,"
718
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
719
- return;
720
- }
721
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
722
- // points so the pixels already land near 200 dpi at print size.
723
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
724
- if (format === "png") {
725
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
726
- } else {
727
- buildPdf(canvas, cssWidth, cssHeight)
728
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
729
- .catch(() => complain("pdf"));
730
- }
731
- }, () => complain(format));
732
- }
733
-
734
- for (const format of Object.keys(buttons)) {
735
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
736
- }
737
- // Embedded, the buttons live in the host's panel header (see main.taExport).
738
- window.addEventListener("message", ev => {
739
- const d = ev.data;
740
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
741
- });
742
- }
743
-
744
- // When embedded, report the content height so the host page can size the
745
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
746
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
747
- // clamped to at least the iframe's current viewport, which turns the
748
- // resize round-trip into a grow-forever feedback loop. The change guard
749
- // stops re-posting once the height settles.
750
- let lastPostedHeight = 0;
751
- function postHeight() {
752
- if (window.parent === window) return;
753
- const height = Math.ceil(document.body.offsetHeight);
754
- if (Math.abs(height - lastPostedHeight) < 3) return;
755
- lastPostedHeight = height;
756
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
757
- }
758
-
759
-
760
- // ---------- geometry ----------
761
- const AXIS_W = 72; // width of the sticky y-axis pane
762
- const PLOT_H = 320; // height of the plot area itself
763
- const TOP = 14; // headroom above the tallest bar
764
- const LABEL_TOP = 18; // axis line -> first label row
765
- const LABEL_ROW = 19; // vertical offset of the staggered second row
766
- const LABEL_SIZE = 14; // method names; the slot below scales with it
767
- const TICK_SIZE = 12.5;
768
- // Method names are set horizontally (rotated ones are markedly harder to
769
- // read), staggered over two rows exactly like the static figure — so a slot
770
- // has to be wide enough for half a name. Past ~24 methods the chart scrolls
771
- // rather than squeezing every column into the viewport.
772
- // No upper bound on the slot: with few methods selected a capped slot left
773
- // the columns huddled on the left with dead space beside them, so the slots
774
- // simply share out whatever width there is.
775
- const MIN_SLOT = 66;
776
- const BAR_FRAC = 0.88; // share of the slot the widest bar takes
777
- const MAX_BAR = 70; // ...but never wider than this, however few columns
778
-
779
- // Variant -> (color token, width relative to the widest bar). The bars are
780
- // concentric, so the nesting itself encodes the tuning progression.
781
- const VARIANT_STYLE = {
782
- "Tuned + Ens.": { color: "var(--var-tunedens)", rel: 1 },
783
- "Tuned": { color: "var(--var-tuned)", rel: 0.8 },
784
- "Default": { color: "var(--var-default)", rel: 0.6 },
785
- };
786
- const VARIANT_ORDER = ["Default", "Tuned", "Tuned + Ens."];
787
- // Dash patterns cycle so several reference lines stay distinguishable.
788
- const REF_DASHES = ["8 5", "2 4", "12 4 3 4"];
789
-
790
- const titleEl = document.getElementById("title");
791
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
792
-
793
- const axisSvg = document.getElementById("axis");
794
- const svg = document.getElementById("chart");
795
- const scroller = document.getElementById("scroller");
796
- const wrap = document.getElementById("chartwrap");
797
- const yminRange = document.getElementById("ymin-range");
798
- const yminVal = document.getElementById("ymin-val");
799
- const tip = makeTooltip(wrap);
800
-
801
- // ---------- data ----------
802
- const METRICS = CONFIG.metrics;
803
- const metricByKey = {};
804
- for (const m of METRICS) metricByKey[m.key] = m;
805
-
806
- // One entry per method (its variants grouped); reference pipelines are kept
807
- // apart — they are drawn as threshold lines, not as columns.
808
- const byMethod = new Map();
809
- const refs = [];
810
- for (const p of POINTS) {
811
- if (p.reference) { refs.push(p); continue; }
812
- let entry = byMethod.get(p.method);
813
- if (!entry) {
814
- entry = { method: p.method, family: p.family, url: p.url, points: [] };
815
- byMethod.set(p.method, entry);
816
- }
817
- entry.points.push(p);
818
- }
819
- for (const e of byMethod.values()) {
820
- e.points.sort((a, b) => VARIANT_ORDER.indexOf(a.variant) - VARIANT_ORDER.indexOf(b.variant));
821
- e.imputed = e.points.some(p => p.imputed);
822
- e.imputed_pct = Math.max(...e.points.map(p => p.imputed_pct || 0));
823
- }
824
-
825
- const state = {
826
- metric: METRICS[0].key,
827
- sort: "best",
828
- yMin: null, // null = the automatic axis floor; a number = zoomed in
829
-
830
- methods: new Set(byMethod.keys()),
831
- refs: new Set(refs.map(r => r.method)),
832
- variants: new Set(VARIANT_ORDER),
833
- };
834
-
835
- function metric() { return metricByKey[state.metric]; }
836
-
837
- // A method's best value under `m`, ignoring variants the reader switched off.
838
- function bestOf(entry, m) {
839
- const vals = entry.points
840
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
841
- .map(p => p[m.key]);
842
- if (!vals.length) return null;
843
- return m.lowerBetter ? Math.min(...vals) : Math.max(...vals);
844
- }
845
-
846
- function sortedMethods(entries, m) {
847
- const arr = [...entries];
848
- const cmp = {
849
- best: (a, b) => rankVal(a, m) - rankVal(b, m),
850
- worst: (a, b) => rankVal(b, m) - rankVal(a, m),
851
- name: (a, b) => a.method.localeCompare(b.method),
852
- family: (a, b) =>
853
- FAM_ORDER.indexOf(a.family) - FAM_ORDER.indexOf(b.family) || rankVal(a, m) - rankVal(b, m),
854
- }[state.sort];
855
- return arr.sort(cmp);
856
- }
857
- // Sort key that puts "better" first for either metric direction, with
858
- // value-less methods last.
859
- function rankVal(entry, m) {
860
- const v = bestOf(entry, m);
861
- if (v == null) return Infinity;
862
- return m.lowerBetter ? v : -v;
863
- }
864
-
865
- function visibleEntries() {
866
- const m = metric();
867
- return sortedMethods([...byMethod.values()].filter(e => state.methods.has(e.method) && bestOf(e, m) != null), m);
868
- }
869
- function visibleRefs() {
870
- return refs.filter(r => state.refs.has(r.method) && r[state.metric] != null);
871
- }
872
-
873
- // Rendered width of each label, measured in the live document (font metrics
874
- // are not knowable up front): it decides whether the names fit on one row or
875
- // need the two-row stagger, and which ones have to be shortened.
876
- function measureLabels(names) {
877
- const probe = el("g", { visibility: "hidden" }, svg);
878
- const widths = names.map(name => {
879
- const t = el("text", { "font-size": LABEL_SIZE }, probe);
880
- t.textContent = name;
881
- return t.getComputedTextLength();
882
- });
883
- probe.remove();
884
- return widths;
885
- }
886
-
887
- // Trim a name to `budget` px, ending in an ellipsis. The full name stays one
888
- // hover (and one data-table row) away.
889
- function fitLabel(node, name, width, budget) {
890
- if (width <= budget) return;
891
- let text = name;
892
- while (text.length > 1 && node.getComputedTextLength() > budget) {
893
- text = text.slice(0, -1);
894
- node.textContent = text + "…";
895
- }
896
- }
897
-
898
- // ---------- chart ----------
899
- function render() {
900
- const m = metric();
901
- const entries = visibleEntries();
902
- const shownRefs = visibleRefs();
903
- svg.textContent = "";
904
- axisSvg.textContent = "";
905
- axisSvg.setAttribute("width", AXIS_W);
906
-
907
- const avail = Math.max(240, scroller.clientWidth - 2);
908
- if (!entries.length) {
909
- axisSvg.setAttribute("height", 120);
910
- svg.setAttribute("width", avail);
911
- svg.setAttribute("height", 120);
912
- const t = el("text", { x: avail / 2, y: 60, "text-anchor": "middle", "font-size": 13, fill: "var(--muted)" }, svg);
913
- t.textContent = "No methods selected — use “All methods” to bring them back.";
914
- buildLegend(m, shownRefs);
915
- postHeight();
916
- return;
917
- }
918
-
919
- const slot = Math.max(MIN_SLOT, avail / entries.length);
920
- const barUnit = Math.min(MAX_BAR, slot * BAR_FRAC);
921
- const plotW = Math.max(avail, slot * entries.length);
922
- // Names go on one row when they fit side by side, otherwise on two
923
- // staggered rows (each label then has two slots of room).
924
- const labels = entries.map(e => e.method + (e.imputed ? " ‡" : ""));
925
- const labelWidths = measureLabels(labels);
926
- const widest = Math.max(...labelWidths);
927
- const labelRows = widest <= slot - 6 ? 1 : 2;
928
- const H = TOP + PLOT_H + LABEL_TOP + (labelRows - 1) * LABEL_ROW + 10;
929
- svg.setAttribute("width", plotW);
930
- svg.setAttribute("height", H);
931
- axisSvg.setAttribute("height", H);
932
-
933
- // -- y domain: from zero where the metric has one, else a floor below the
934
- // shortest bar (Elo has no meaningful zero, and starting at zero would
935
- // squash every difference into the top fifth of the chart). The floor
936
- // ignores the CI whiskers so one wide interval cannot deflate the scale.
937
- const barVals = [];
938
- const allVals = [];
939
- for (const e of entries) {
940
- for (const p of e.points) {
941
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
942
- barVals.push(p[m.key]);
943
- allVals.push(p[m.key]);
944
- if (m.ci && p[m.ci.hi] != null) allVals.push(p[m.ci.hi]);
945
- }
946
- }
947
- for (const r of shownRefs) { barVals.push(r[state.metric]); allVals.push(r[state.metric]); }
948
- const barMin = Math.min(...barVals), barMax = Math.max(...allVals);
949
- const span = barMax - barMin || Math.abs(barMax) || 1;
950
- let autoY0, y1;
951
- if (m.fromZero) {
952
- autoY0 = 0;
953
- y1 = barMax * 1.06 || 1;
954
- } else {
955
- // Snap the floor down to a tick multiple so the axis reads in round
956
- // numbers, and keep it just below the shortest bar: every bar stays
957
- // visible (the static figure clips the ones below its fixed floor).
958
- const step = niceStep(span / 6);
959
- autoY0 = Math.floor((barMin - span * 0.05) / step) * step;
960
- y1 = barMax + span * 0.04;
961
- }
962
- // The zoom slider raises the floor from there toward the top of the field,
963
- // magnifying the differences between the leaders. Its bounds follow the
964
- // metric's own scale, so they are refreshed on every render; the bars and
965
- // whiskers that drop below the new floor are clipped away (see plot-clip).
966
- const zoomMax = autoY0 + (y1 - autoY0) * 0.9;
967
- yminRange.min = autoY0;
968
- yminRange.max = zoomMax;
969
- yminRange.step = (zoomMax - autoY0) / 200 || 1;
970
- if (state.yMin != null) state.yMin = Math.min(state.yMin, zoomMax);
971
- if (state.yMin != null && state.yMin <= autoY0) state.yMin = null;
972
- const y0 = state.yMin == null ? autoY0 : state.yMin;
973
- yminRange.value = y0;
974
- yminVal.textContent = state.yMin == null ? "auto" : fmtNum(y0, m.decimals);
975
- const Y = v => TOP + (1 - (v - y0) / (y1 - y0)) * PLOT_H;
976
- const baseY = Y(y0);
977
-
978
- // -- grid (solid hairlines) in the plot pane, tick labels in the axis pane
979
- const tickVals = ticks(y0, y1, 6);
980
- const grid = el("g", {}, svg);
981
- const tickLabels = [];
982
- for (const tv of tickVals) {
983
- const gy = Y(tv);
984
- el("line", { x1: 0, y1: gy, x2: plotW, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
985
- const t = el("text", {
986
- x: AXIS_W - 10, y: gy + 4, "text-anchor": "end", "font-size": TICK_SIZE, fill: "var(--muted)",
987
- }, axisSvg);
988
- t.textContent = fmtNum(tv, m.decimals);
989
- tickLabels.push({ y: gy, node: t });
990
- }
991
- el("line", { x1: 0, y1: baseY, x2: plotW, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, grid);
992
- el("line", { x1: AXIS_W - 1, y1: TOP, x2: AXIS_W - 1, y2: baseY, stroke: "var(--muted)", "stroke-width": 1 }, axisSvg);
993
- el("text", {
994
- x: 0, y: 0, "text-anchor": "middle", "font-size": LABEL_SIZE, "font-weight": 650,
995
- fill: "var(--ink)", transform: `translate(15 ${TOP + PLOT_H / 2}) rotate(-90)`,
996
- }, axisSvg).textContent = m.axisLabel;
997
-
998
- // -- hover band behind the marks (highlights the whole column group)
999
- const band = el("rect", {
1000
- x: 0, y: TOP, width: slot, height: PLOT_H + 6, fill: "var(--ink)", opacity: 0, "pointer-events": "none",
1001
- }, svg);
1002
-
1003
- const defs = el("defs", {}, svg);
1004
- // -- imputed hatch, reused by every partially imputed bar. Crossed diagonals,
1005
- // matching the "x" hatch matplotlib draws in the static figure; the
1006
- // diagonals meet the tile corners exactly, so the grid tiles seamlessly.
1007
- const pat = el("pattern", {
1008
- id: "imp-hatch", width: 8, height: 8, patternUnits: "userSpaceOnUse",
1009
- }, defs);
1010
- el("path", {
1011
- d: "M0,0 L8,8 M8,0 L0,8", stroke: "var(--paper)", "stroke-width": 1.5,
1012
- opacity: 0.85, fill: "none",
1013
- }, pat);
1014
-
1015
- // -- Everything data-bearing is clipped to the plot area. The axis floor is
1016
- // set from the bar values (a couple of very wide intervals would
1017
- // otherwise deflate the whole scale), so a long lower CI whisker can
1018
- // reach past it and run into the method names below. Clip it there; the
1019
- // exact interval stays in the tooltip and the data table.
1020
- const clip = el("clipPath", { id: "plot-clip" }, defs);
1021
- el("rect", { x: 0, y: 0, width: plotW, height: baseY }, clip);
1022
-
1023
- // -- bars: one concentric group per method
1024
- const barsG = el("g", { "clip-path": "url(#plot-clip)" }, svg);
1025
- entries.forEach((entry, i) => {
1026
- const cx = i * slot + slot / 2;
1027
- // Widest bar first, narrowest last: the bars are concentric, so painting
1028
- // them by width guarantees each one stays visible. Ordering by height
1029
- // instead loses a variant outright whenever a narrower bar is the taller
1030
- // of the two (TabSTAR's tuned bar sat 1 Elo above tuned + ensembled, and
1031
- // the wider bar covered it completely).
1032
- const relOf = p => (VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"]).rel;
1033
- const drawn = entry.points
1034
- .filter(p => state.variants.has(p.variant) && p[m.key] != null)
1035
- .slice()
1036
- .sort((a, b) => relOf(b) - relOf(a));
1037
- for (const p of drawn) {
1038
- const style = VARIANT_STYLE[p.variant] || VARIANT_STYLE["Default"];
1039
- const w = barUnit * style.rel;
1040
- const top = Y(p[m.key]);
1041
- const rect = {
1042
- x: cx - w / 2, y: Math.min(top, baseY), width: w, height: Math.max(1, Math.abs(baseY - top)),
1043
- };
1044
- // 2px surface ring, not a border: it is what keeps a nested bar legible
1045
- // against the wider bar it sits inside.
1046
- el("rect", { ...rect, fill: style.color, stroke: "var(--paper)", "stroke-width": 1.5, rx: 2 }, barsG);
1047
- if (p.imputed) el("rect", { ...rect, fill: "url(#imp-hatch)", rx: 2 }, barsG);
1048
- if (m.ci && p[m.ci.lo] != null) {
1049
- const cap = Math.max(2.5, w * 0.34);
1050
- const whisk = el("g", {
1051
- stroke: `color-mix(in srgb, ${style.color} 55%, var(--ink))`, "stroke-width": 1.4, opacity: 0.9,
1052
- }, barsG);
1053
- el("line", { x1: cx, y1: Y(p[m.ci.lo]), x2: cx, y2: Y(p[m.ci.hi]) }, whisk);
1054
- el("line", { x1: cx - cap, y1: Y(p[m.ci.hi]), x2: cx + cap, y2: Y(p[m.ci.hi]) }, whisk);
1055
- el("line", { x1: cx - cap, y1: Y(p[m.ci.lo]), x2: cx + cap, y2: Y(p[m.ci.lo]) }, whisk);
1056
- }
1057
- }
1058
- });
1059
-
1060
- // -- x axis: the method name, colored by model family (the legend below
1061
- // names the colors).
1062
- const xg = el("g", {}, svg);
1063
- // Hairlines share one group that is painted before every name, so a name
1064
- // nudged sideways at the edge (see below) passes over a neighbouring
1065
- // column's hairline instead of being crossed out by it.
1066
- const xLines = el("g", {}, xg);
1067
- const xLabels = el("g", {}, xg);
1068
- entries.forEach((entry, i) => {
1069
- const cx = i * slot + slot / 2;
1070
- const row = labelRows === 1 ? 0 : i % 2;
1071
- const y = baseY + LABEL_TOP + row * LABEL_ROW;
1072
- // A hairline drops the staggered row back to its own column.
1073
- if (row) {
1074
- el("line", {
1075
- x1: cx, y1: baseY + 3, x2: cx, y2: y - 10,
1076
- stroke: "var(--line)", "stroke-width": 1,
1077
- }, xLines);
1078
- }
1079
- const t = el("text", {
1080
- x: cx, y, "font-size": LABEL_SIZE, "text-anchor": "middle", "font-weight": 650,
1081
- fill: FAM_INK[entry.family] || "var(--ink)",
1082
- // A halo in the surface color keeps the glyphs legible where a nudged
1083
- // name crosses a hairline, rather than the line running through them.
1084
- "paint-order": "stroke", stroke: "var(--paper)", "stroke-width": 3,
1085
- }, xLabels);
1086
- t.textContent = labels[i];
1087
- fitLabel(t, labels[i], labelWidths[i], slot * labelRows - 8);
1088
- // The outermost columns sit only half a slot from the edge, so a name
1089
- // wider than one slot would reach past the SVG viewport and be cut off
1090
- // there (a name may occupy two slots when the rows are staggered). Nudge
1091
- // it inwards by just enough to stay whole; every other label keeps its
1092
- // column centre, and the bar below still marks the column.
1093
- const half = t.getComputedTextLength() / 2;
1094
- t.setAttribute("x", Math.max(half + 1, Math.min(cx, plotW - half - 1)));
1095
- });
1096
-
1097
- // -- reference pipelines as threshold lines. Their names live in the legend
1098
- // (matched by dash pattern) rather than on the line, where they would
1099
- // cover the tallest bars at every scroll position.
1100
- const refG = el("g", {}, svg);
1101
- const tagYs = [];
1102
- shownRefs.forEach((r, i) => {
1103
- const ry = Y(r[state.metric]);
1104
- if (ry < TOP || ry > baseY) return;
1105
- el("line", {
1106
- x1: 0, y1: ry, x2: plotW, y2: ry, stroke: "var(--fam-reference)", "stroke-width": 1.8,
1107
- "stroke-dasharray": REF_DASHES[i % REF_DASHES.length], opacity: 0.95,
1108
- }, refG);
1109
- // Sticky value tag in the axis pane, so the threshold stays readable at
1110
- // any scroll position. Two nearby thresholds would print on top of each
1111
- // other, so nudge each tag clear of the ones already placed; a tag wins
1112
- // over a tick label it would sit on.
1113
- let ty = ry;
1114
- while (tagYs.some(y => Math.abs(y - ty) < 12)) ty += 12;
1115
- tagYs.push(ty);
1116
- for (const t of tickLabels) {
1117
- if (Math.abs(t.y - ty) < 10) t.node.remove();
1118
- }
1119
- el("text", {
1120
- x: AXIS_W - 10, y: ty + 4, "text-anchor": "end", "font-size": 11, "font-weight": 650,
1121
- fill: "var(--fam-reference)",
1122
- }, axisSvg).textContent = fmtMetric(m, r[state.metric]);
1123
- });
1124
- buildLegend(m, shownRefs);
1125
-
1126
- // -- hit targets: one full-height column per method (>= 34px wide)
1127
- const hits = el("g", {}, svg);
1128
- entries.forEach((entry, i) => {
1129
- const h = el("rect", {
1130
- x: i * slot, y: TOP, width: slot, height: PLOT_H + 6, fill: "transparent", cursor: "pointer",
1131
- }, hits);
1132
- h.addEventListener("mouseenter", ev => {
1133
- band.setAttribute("x", i * slot);
1134
- band.setAttribute("opacity", 0.06);
1135
- showTip(entry, ev);
1136
- });
1137
- h.addEventListener("mousemove", ev => tip.move(ev));
1138
- h.addEventListener("mouseleave", () => { band.setAttribute("opacity", 0); tip.hide(); });
1139
- h.addEventListener("click", () => toggleMethod(entry.method));
1140
- });
1141
-
1142
- postHeight();
1143
- }
1144
-
1145
- function showTip(entry, ev) {
1146
- const m = metric();
1147
- let html = `<div class="t-name">${entry.method}</div><div>${entry.family}</div>`;
1148
- for (const p of entry.points) {
1149
- if (!state.variants.has(p.variant) || p[m.key] == null) continue;
1150
- const ci = m.ci && p[m.ci.lo] != null
1151
- ? ` <span class="t-var">(${fmtNum(p[m.ci.lo], m.decimals)}–${fmtNum(p[m.ci.hi], m.decimals)})</span>`
1152
- : "";
1153
- html += `<div><span class="t-var">${p.variant}:</span> <b>${fmtMetric(m, p[m.key])}</b>${ci}</div>`;
1154
- }
1155
- if (entry.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(entry.imputed_pct, 0)}% of datasets</div>`;
1156
- tip.show(html, ev);
1157
- }
1158
-
1159
- // ---------- chips ----------
1160
- const chipsBox = document.getElementById("chips");
1161
- const chipByMethod = new Map();
1162
- const famChips = new Map();
1163
-
1164
- function familyMembers(fam) {
1165
- const out = [...byMethod.values()].filter(e => e.family === fam).map(e => e.method);
1166
- for (const r of refs) if (r.family === fam) out.push(r.method);
1167
- return out;
1168
- }
1169
- function isOn(name) { return state.methods.has(name) || state.refs.has(name); }
1170
-
1171
- function buildChips() {
1172
- const rankMetric = metricByKey[CONFIG.rankMetric] || METRICS[0];
1173
- const head = document.createElement("div");
1174
- head.className = "chips-head";
1175
- head.textContent = "Methods shown — click to remove, click a family to toggle the whole group";
1176
- chipsBox.appendChild(head);
1177
- for (const fam of FAM_ORDER) {
1178
- const members = familyMembers(fam);
1179
- if (!members.length) continue;
1180
- members.sort((a, b) => chipRank(a, rankMetric) - chipRank(b, rankMetric));
1181
- const row = document.createElement("div");
1182
- row.className = "chiprow";
1183
- const famBtn = document.createElement("button");
1184
- famBtn.className = "famchip";
1185
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1186
- famBtn.innerHTML = famChipLabel(fam, members.length);
1187
- famBtn.title = `Toggle all ${members.length} ${fam} methods`;
1188
- famBtn.addEventListener("click", () => toggleFamily(fam));
1189
- row.appendChild(famBtn);
1190
- famChips.set(fam, famBtn);
1191
- const set = document.createElement("div");
1192
- set.className = "chipset";
1193
- for (const name of members) {
1194
- const entry = byMethod.get(name);
1195
- const b = document.createElement("button");
1196
- b.className = "chip";
1197
- b.style.setProperty("--fam", FAM_VAR[fam]);
1198
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1199
- b.appendChild(Object.assign(document.createElement("span"), { textContent: name }));
1200
- if (entry && entry.imputed) {
1201
- const mark = document.createElement("span");
1202
- mark.className = "imp-mark";
1203
- mark.textContent = "‡";
1204
- b.appendChild(mark);
1205
- }
1206
- b.title = name + (entry && entry.imputed ? " — partially imputed" : "");
1207
- b.addEventListener("click", () => toggleMethod(name));
1208
- set.appendChild(b);
1209
- chipByMethod.set(name, b);
1210
- }
1211
- row.appendChild(set);
1212
- chipsBox.appendChild(row);
1213
- }
1214
- }
1215
- function chipRank(name, m) {
1216
- const entry = byMethod.get(name);
1217
- if (entry) return rankVal(entry, m);
1218
- const r = refs.find(x => x.method === name);
1219
- const v = r ? r[m.key] : null;
1220
- return v == null ? Infinity : (m.lowerBetter ? v : -v);
1221
- }
1222
- function syncChips() {
1223
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(isOn(name)));
1224
- for (const [fam, b] of famChips) b.setAttribute("aria-pressed", String(familyMembers(fam).every(isOn)));
1225
- }
1226
-
1227
- function toggleMethod(name) {
1228
- const set = byMethod.has(name) ? state.methods : state.refs;
1229
- if (set.has(name)) set.delete(name); else set.add(name);
1230
- syncChips();
1231
- render();
1232
- }
1233
- function toggleFamily(fam) {
1234
- const members = familyMembers(fam);
1235
- const allOn = members.every(isOn);
1236
- for (const name of members) {
1237
- const set = byMethod.has(name) ? state.methods : state.refs;
1238
- if (allOn) set.delete(name); else set.add(name);
1239
- }
1240
- syncChips();
1241
- render();
1242
- }
1243
- function setMethods(names) {
1244
- state.methods = new Set(names);
1245
- syncChips();
1246
- render();
1247
- }
1248
-
1249
- document.getElementById("btn-all").addEventListener("click", () => {
1250
- state.refs = new Set(refs.map(r => r.method));
1251
- setMethods(byMethod.keys());
1252
- });
1253
- document.getElementById("btn-none").addEventListener("click", () => {
1254
- state.refs = new Set();
1255
- setMethods([]);
1256
- });
1257
- document.getElementById("btn-top").addEventListener("click", () => {
1258
- const m = metric();
1259
- // Rank explicitly rather than reusing the display sort: which 15 methods are
1260
- // kept must not depend on which end of the axis the best ones are drawn at
1261
- // (sorting "best on the right" would otherwise select the 15 worst).
1262
- const top = [...byMethod.values()]
1263
- .sort((a, b) => rankVal(a, m) - rankVal(b, m))
1264
- .slice(0, 15)
1265
- .map(e => e.method);
1266
- state.refs = new Set(refs.map(r => r.method));
1267
- setMethods(top);
1268
- });
1269
-
1270
- // ---------- selectors ----------
1271
- yminRange.addEventListener("input", ev => {
1272
- state.yMin = Number(ev.target.value);
1273
- render();
1274
- });
1275
-
1276
- const metricSelect = document.getElementById("metric-select");
1277
- for (const m of METRICS) {
1278
- metricSelect.appendChild(Object.assign(document.createElement("option"), { value: m.key, textContent: m.label }));
1279
- }
1280
- metricSelect.addEventListener("change", ev => {
1281
- state.metric = ev.target.value;
1282
- state.yMin = null; // the previous floor means nothing on a new scale
1283
- buildTable();
1284
- render();
1285
- });
1286
-
1287
- const sortSelect = document.getElementById("sort-select");
1288
- for (const [value, label] of [
1289
- ["best", "Best on the left"], ["worst", "Best on the right"],
1290
- ["family", "Model family"], ["name", "A–Z"],
1291
- ]) {
1292
- sortSelect.appendChild(Object.assign(document.createElement("option"), { value, textContent: label }));
1293
- }
1294
- sortSelect.value = state.sort;
1295
- sortSelect.addEventListener("change", ev => { state.sort = ev.target.value; render(); });
1296
-
1297
- // Variant toggles: buttons rather than chips, since they filter the series
1298
- // rather than the rows. "Default" gets no button at all — every method has a
1299
- // default result, so it is the baseline of the chart rather than an option
1300
- // (switching it off would leave the default-only methods with no bar). Only
1301
- // the extras layered on top, tuning and ensembling, are toggleable; the legend
1302
- // still carries Default's color.
1303
- const ALWAYS_SHOWN = "Default";
1304
- const variantBtns = document.getElementById("variant-btns");
1305
- const variantBtnByKey = new Map();
1306
- for (const v of VARIANT_ORDER) {
1307
- if (v === ALWAYS_SHOWN || !POINTS.some(p => p.variant === v)) continue;
1308
- const b = document.createElement("button");
1309
- b.className = "btn";
1310
- b.textContent = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1311
- b.style.setProperty("--fam", VARIANT_STYLE[v].color);
1312
- b.title = `Show or hide the ${b.textContent.toLowerCase()} bars`;
1313
- b.addEventListener("click", () => {
1314
- if (state.variants.has(v)) state.variants.delete(v);
1315
- else state.variants.add(v);
1316
- syncVariantBtns();
1317
- render();
1318
- });
1319
- variantBtns.appendChild(b);
1320
- variantBtnByKey.set(v, b);
1321
- }
1322
- function syncVariantBtns() {
1323
- for (const [v, b] of variantBtnByKey) {
1324
- const on = state.variants.has(v);
1325
- b.setAttribute("aria-pressed", String(on));
1326
- b.style.opacity = on ? "1" : "0.45";
1327
- b.style.borderColor = on ? VARIANT_STYLE[v].color : "var(--line)";
1328
- }
1329
- }
1330
-
1331
- // ---------- legend ----------
1332
- // Rebuilt on every render: it names the reference lines (each by its dash
1333
- // pattern and current value), which change with the metric and the selection.
1334
- function buildLegend(m, shownRefs) {
1335
- const parts = [];
1336
- for (const v of VARIANT_ORDER) {
1337
- if (!POINTS.some(p => p.variant === v)) continue;
1338
- const label = v === "Tuned + Ens." ? "Tuned + Ensembled" : v;
1339
- const off = state.variants.has(v) ? "" : ' style="opacity:0.4"';
1340
- parts.push(
1341
- `<span class="item"${off}><svg width="12" height="12" viewBox="0 0 12 12">` +
1342
- `<rect x="1" y="1" width="10" height="10" rx="2" fill="${VARIANT_STYLE[v].color}"/></svg> ${label}</span>`);
1343
- }
1344
- if (m.ci) {
1345
- parts.push('<span class="item"><svg width="12" height="14" viewBox="0 0 12 14">' +
1346
- '<path d="M6,2 V12 M2,2 H10 M2,12 H10" stroke="var(--muted)" stroke-width="1.4" fill="none"/></svg> 95% CI</span>');
1347
- }
1348
- shownRefs.forEach((r, i) => {
1349
- parts.push(`<span class="item"><svg width="26" height="8" viewBox="0 0 26 8">` +
1350
- `<line x1="0" y1="4" x2="26" y2="4" stroke="var(--fam-reference)" stroke-width="1.8" ` +
1351
- `stroke-dasharray="${REF_DASHES[i % REF_DASHES.length]}"/></svg> ${r.method} · ${fmtMetric(m, r[state.metric])}</span>`);
1352
- });
1353
- if (POINTS.some(p => p.imputed)) {
1354
- parts.push('<span class="item"><svg width="14" height="14" viewBox="0 0 14 14">' +
1355
- '<rect x="1" y="1" width="12" height="12" rx="2" fill="var(--pt-muted)"/>' +
1356
- '<path d="M1,1 L7,7 M7,1 L1,7 M7,7 L13,13 M13,7 L7,13 M1,7 L7,13 M7,7 L13,1" ' +
1357
- 'stroke="var(--paper)" stroke-width="1.5" fill="none"/>' +
1358
- "</svg> &Dagger; partially imputed</span>");
1359
- }
1360
- // Model family, named rather than merely pointed at: without the colors
1361
- // spelled out, the swatch under each column decodes to nothing.
1362
- const families = FAM_ORDER.filter(f => POINTS.some(p => !p.reference && p.family === f));
1363
- if (families.length > 1) {
1364
- parts.push('<span class="legendbreak"></span><span class="item">Family:</span>');
1365
- for (const fam of families) {
1366
- parts.push(`<span class="item"><svg width="16" height="9" viewBox="0 0 16 9">` +
1367
- `<rect x="0" y="1" width="16" height="7" rx="2" fill="${FAM_VAR[fam]}"/></svg> ` +
1368
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`);
1369
- }
1370
- }
1371
- document.getElementById("legendstrip").innerHTML = parts.join("");
1372
- }
1373
-
1374
- // ---------- data table (the WCAG-clean twin of the chart) ----------
1375
- function buildTable() {
1376
- const m = metric();
1377
- const rows = [...POINTS].filter(p => p[m.key] != null).sort((a, b) =>
1378
- m.lowerBetter ? a[m.key] - b[m.key] : b[m.key] - a[m.key]);
1379
- let html = "<table><thead><tr><th>Method</th><th>Variant</th><th>Family</th>";
1380
- for (const x of METRICS) html += `<th>${x.label}</th>`;
1381
- html += "<th>Imputed</th></tr></thead><tbody>";
1382
- for (const p of rows) {
1383
- html += `<tr><td>${p.method}</td><td>${p.variant || "—"}</td><td>${p.family}</td>`;
1384
- for (const x of METRICS) html += `<td>${fmtMetric(x, p[x.key])}</td>`;
1385
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1386
- }
1387
- document.getElementById("tblwrap").innerHTML = html + "</tbody></table>";
1388
- }
1389
-
1390
- // ---------- paper view ----------
1391
- setUpPaperView(render);
1392
- // The y-axis lives in its own pane, so it is offset back into place.
1393
- setUpExport(() => [{ svg: axisSvg, dx: 0 }, { svg: svg, dx: AXIS_W }], () => slugify(document.title));
1394
-
1395
- // ---------- boot ----------
1396
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1397
- let resizeTimer = null;
1398
- window.addEventListener("resize", () => {
1399
- clearTimeout(resizeTimer);
1400
- resizeTimer = setTimeout(render, 120);
1401
- });
1402
-
1403
- buildChips();
1404
- buildTable();
1405
- syncChips();
1406
- syncVariantBtns();
1407
- render(); // also builds the legend (it depends on the metric + selection)
1408
- })();
1409
- </script>
1410
- </body>
1411
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/leaderboard_table.html DELETED
@@ -1,1162 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena leaderboard table — no imputation | All Tasks | Small</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* --- The table ------------------------------------------------------------
269
- A scroll box with a sticky header, so the column names stay put through 80
270
- rows. Numbers are tabular so digits line up down a column. */
271
- .lbt-scroll {
272
- overflow: auto; max-height: 720px; margin-top: 4px;
273
- border: 1px solid var(--line); border-radius: 10px;
274
- scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent;
275
- }
276
- .lbt-scroll::-webkit-scrollbar { width: 11px; height: 11px; }
277
- .lbt-scroll::-webkit-scrollbar-thumb {
278
- background: var(--pt-muted); border-radius: 8px;
279
- border: 3px solid transparent; background-clip: content-box;
280
- }
281
- table.lbt { border-collapse: collapse; width: 100%; font-variant-numeric: tabular-nums; }
282
- table.lbt th, table.lbt td { padding: 5px 9px; text-align: center; border: 1px solid var(--line); }
283
- table.lbt thead th {
284
- position: sticky; top: 0; z-index: 3; background: var(--card); font-size: 12px;
285
- font-weight: 650; white-space: nowrap; cursor: pointer; user-select: none;
286
- box-shadow: inset 0 -1px 0 var(--line);
287
- }
288
- /* The header is both the sort control and the definition, so it gets the
289
- pointer of the former and the dotted underline of the latter. */
290
- table.lbt thead th .lbl { text-decoration: underline; text-decoration-style: dotted;
291
- text-decoration-color: var(--muted); text-underline-offset: 3px; }
292
- table.lbt thead th:hover { color: var(--accent); }
293
- table.lbt thead th::after { content: "\2195"; font-size: 0.72em; opacity: 0.3; margin-left: 5px; }
294
- table.lbt thead th[aria-sort="ascending"]::after { content: "\25B2"; opacity: 0.85; }
295
- table.lbt thead th[aria-sort="descending"]::after { content: "\25BC"; opacity: 0.85; }
296
- table.lbt td.name { text-align: left; white-space: nowrap; }
297
- table.lbt td.pos { color: var(--muted); font-variant-numeric: tabular-nums; }
298
- table.lbt td.type { white-space: nowrap; width: 1%; }
299
- table.lbt td.num { font-weight: 600; }
300
- table.lbt td.na { color: var(--pt-muted); }
301
- table.lbt tbody tr:hover td { filter: brightness(1.12); }
302
- .ci { opacity: 0.5; font-weight: 400; font-size: 0.82em; }
303
- .pill { padding: 1px 7px; border-radius: 999px; font-size: 0.95em; white-space: nowrap; }
304
- .verified { font-size: 0.85em; }
305
- .link-icon { font-size: 0.78em; opacity: 0.65; margin-left: 2px; }
306
- /* Grey rather than a dimmed inherit: dimming leaves the tag tinted with the
307
- family colour, which the model name already carries, and made this table
308
- disagree with the cross-subset table on the app page. */
309
- .variant-tag { color: var(--muted); font-weight: 400; font-size: 0.9em; }
310
- td.name .imp-mark { color: var(--muted); font-weight: 700; margin-left: 3px; }
311
- td.name a { text-decoration: underline; text-decoration-style: dotted; text-underline-offset: 3px; }
312
- td.name a:hover { text-decoration-style: solid; }
313
-
314
- .lbt-search {
315
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
316
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
317
- padding: 6px 9px; width: 12em;
318
- }
319
- .lbt-cap { font-size: 12.5px; color: var(--muted); margin: 7px 0 0; }
320
- .lbt-empty { padding: 34px 0; text-align: center; color: var(--muted); font-size: 13px; }
321
- .chips-head { flex: 1 1 100%; font-size: 12.5px; color: var(--muted); font-weight: 600; }
322
- /* The model selector sits above the table it filters. Families pack side by
323
- side rather than stacking: one row each would leave the small ones (Baseline,
324
- Other) wasting a line. */
325
- .chips { margin: 2px 0 10px; flex-direction: row; flex-wrap: wrap; gap: 12px 28px; align-items: flex-start; }
326
- .chiprow { flex: 0 1 auto; min-width: 0; }
327
-
328
- /* The data export, in the same green the website's figure-export controls use,
329
- so "this downloads something" reads the same across the site. */
330
- .btn.export {
331
- color: #063; background: #7ee0b8; border-color: #7ee0b8; font-weight: 700;
332
- }
333
- .btn.export:hover { background: #a6efcd; border-color: #a6efcd; }
334
- /* Paper view keeps the table: here the table *is* the figure. */
335
- body.paper .lbt-scroll { max-height: none; }
336
- </style>
337
- </head>
338
- <body>
339
- <div class="viewbar">
340
- <button class="btn" id="btn-paper" title="White background, table only — for slides and papers">Paper view</button>
341
- </div>
342
- <p class="explorer-title" id="title"></p>
343
- <div class="controls">
344
- <span class="grouplabel">Variants</span>
345
- <div class="btnrow" id="variant-btns"></div>
346
- <button class="btn" id="btn-imputed" title="Models whose score is partly imputed, marked &#8225;"></button>
347
- <input class="lbt-search" id="search" type="search" placeholder="Search model or family" aria-label="Search">
348
- <span class="grouplabel">Columns</span>
349
- <div class="btnrow" id="col-btns"></div>
350
- <button class="btn export" id="btn-csv" title="Download the rows and columns shown, in the current sort order">Download CSV</button>
351
- </div>
352
- <!-- The shared paper-view helper reveals this bar; a table has no figure to
353
- export as SVG/PNG/PDF, and its CSV button belongs beside the filters that
354
- shape the export, so it stays empty here. -->
355
- <div class="exportbar" id="exportbar" hidden></div>
356
- <!-- The model selector belongs above the table it filters. It doubles as the
357
- family key, which is why there is no separate legend strip. -->
358
- <div class="chips" id="chips"></div>
359
- <div class="lbt-scroll" id="tblwrap"></div>
360
- <p class="lbt-cap" id="caption"></p>
361
-
362
- <script>
363
- (function () {
364
- "use strict";
365
- const CONFIG = {"title": null, "columns": [{"key": "position", "label": "#", "always": true, "hint": "Position in this subset's published ranking."}, {"key": "family", "label": "Type", "always": true, "hint": "Model family \u2014 see the key above the table."}, {"key": "model", "label": "Model", "always": true, "hint": "The model, its tuning variant in brackets, \u2714\ufe0f when the implementation was verified, and \u2021 when some results are imputed."}, {"key": "elo", "label": "Elo", "decimals": 0, "heatmap": true, "hint": "Pairwise win-rate rating; a 400-point gap is about a 91% win rate. Higher is better."}, {"key": "score", "label": "Score", "decimals": 3, "heatmap": true, "hint": "Error rescaled per dataset to 1 (best) \u2026 0 (median), then averaged. Higher is better."}, {"key": "rank", "label": "Rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Mean rank across datasets. Lower is better."}, {"key": "hrank", "label": "Harmonic rank", "short": "H. rank", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "Harmonic mean of per-dataset ranks; rewards being excellent somewhere. Lower is better."}, {"key": "imp", "label": "Improvability (%)", "short": "Improv.", "decimals": 2, "lowerBetter": true, "heatmap": true, "hint": "How much lower the best model's error is than this one's, per dataset. Lower is better."}, {"key": "train_time", "label": "Train time (s/1K)", "short": "Train s", "decimals": 2, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to fit per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "predict_time", "label": "Predict time (s/1K)", "short": "Predict s", "decimals": 3, "lowerBetter": true, "heatmap": true, "logScale": true, "hint": "Median seconds to predict per 1000 rows. Lower is better; shaded on a log scale."}, {"key": "imputed_pct", "label": "Imputed (%)", "short": "Imputed", "decimals": 1, "lowerBetter": true, "heatmap": true, "hint": "Share of datasets whose score was imputed because the model could not run on them."}, {"key": "hardware", "label": "Hardware", "text": true, "hint": "The hardware the reported runtimes were measured on."}], "rankKey": "elo", "variants": ["Default", "Tuned", "Tuned + Ens."]};
366
- const POINTS = [{"position":0,"method":"TabFM","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/google-research\/tabfm","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1744.0,"score":0.897,"rank":6.9,"hrank":1.74,"imp":4.654,"train_time":39.57,"predict_time":5.358,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+142\/-93","imputed":false},{"position":1,"method":"AutoGluon 1.5 (extreme, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1638.0,"score":0.642,"rank":10.67,"hrank":4.73,"imp":9.279,"train_time":346.58,"predict_time":6.557,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-52","imputed":false},{"position":2,"method":"TabPFN-3","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2605.13986","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1636.0,"score":0.675,"rank":10.77,"hrank":4.53,"imp":10.766,"train_time":9.17,"predict_time":0.416,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+80\/-56","imputed":false},{"position":3,"method":"TabPFN-2.6","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1599.0,"score":0.616,"rank":12.44,"hrank":6.63,"imp":11.961,"train_time":7.03,"predict_time":0.548,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+69\/-47","imputed":false},{"position":4,"method":"RealTabPFN-2.5","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1595.0,"score":0.593,"rank":12.63,"hrank":5.6,"imp":11.198,"train_time":2289.39,"predict_time":8.048,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+86\/-62","imputed":false},{"position":5,"method":"TabICLv2","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2602.11139","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1572.0,"score":0.633,"rank":13.78,"hrank":5.25,"imp":11.56,"train_time":3.93,"predict_time":0.152,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+106\/-78","imputed":false},{"position":6,"method":"RealTabPFN-2.5","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1555.0,"score":0.547,"rank":14.67,"hrank":6.51,"imp":11.907,"train_time":2289.39,"predict_time":1.143,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+69\/-56","imputed":false},{"position":7,"method":"RealTabPFN-2.5","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.08667","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1542.0,"score":0.536,"rank":15.41,"hrank":8.41,"imp":12.32,"train_time":6.76,"predict_time":0.636,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+80\/-49","imputed":false},{"position":8,"method":"RealMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1480.0,"score":0.402,"rank":19.13,"hrank":11.38,"imp":14.533,"train_time":3771.85,"predict_time":21.902,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+62\/-44","imputed":false},{"position":9,"method":"TabDPT-Turbo","variant":"Default","family":"Foundation Model","url":"https:\/\/openreview.net\/pdf?id=Y00pwFyrHR","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1448.0,"score":0.391,"rank":21.19,"hrank":9.11,"imp":15.33,"train_time":4.69,"predict_time":0.192,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-54","imputed":false},{"position":10,"method":"TabDPT","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1448.0,"score":0.398,"rank":21.22,"hrank":9.27,"imp":15.246,"train_time":5119.78,"predict_time":218.453,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-60","imputed":false},{"position":11,"method":"AutoGluon 1.4 (best, 4h)","variant":"","family":"Reference Pipeline","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83d\udcca","verified":true,"elo":1444.0,"score":0.326,"rank":21.52,"hrank":13.45,"imp":15.598,"train_time":3716.57,"predict_time":2.547,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-46","imputed":false},{"position":12,"method":"TabM","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1429.0,"score":0.325,"rank":22.52,"hrank":12.22,"imp":15.675,"train_time":2585.58,"predict_time":3.838,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+52\/-48","imputed":false},{"position":13,"method":"TabDPT","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1415.0,"score":0.356,"rank":23.49,"hrank":10.85,"imp":15.884,"train_time":5119.78,"predict_time":28.351,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+70\/-58","imputed":false},{"position":14,"method":"LimiX","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2509.03505","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1405.0,"score":0.353,"rank":24.21,"hrank":9.41,"imp":15.155,"train_time":35.51,"predict_time":6.828,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-72","imputed":false},{"position":15,"method":"RealMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1400.0,"score":0.293,"rank":24.6,"hrank":13.5,"imp":15.91,"train_time":3771.85,"predict_time":1.778,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+51\/-43","imputed":false},{"position":16,"method":"LightGBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1390.0,"score":0.235,"rank":25.34,"hrank":19.43,"imp":16.937,"train_time":892.49,"predict_time":2.57,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+34\/-34","imputed":false},{"position":17,"method":"TabM","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1373.0,"score":0.258,"rank":26.55,"hrank":14.04,"imp":16.561,"train_time":2585.58,"predict_time":0.436,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+53\/-48","imputed":false},{"position":18,"method":"CatBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1368.0,"score":0.211,"rank":26.99,"hrank":19.52,"imp":17.041,"train_time":1739.17,"predict_time":0.396,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+45\/-39","imputed":false},{"position":19,"method":"TabSwift","variant":"Default","family":"Foundation Model","url":"https:\/\/github.com\/LAMDA-Tabular\/TabSwift","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1363.0,"score":0.29,"rank":27.32,"hrank":15.91,"imp":17.224,"train_time":2.76,"predict_time":0.058,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+57\/-49","imputed":false},{"position":20,"method":"iLTM","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1361.0,"score":0.199,"rank":27.46,"hrank":18.84,"imp":17.421,"train_time":22811.74,"predict_time":841.446,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+44\/-39","imputed":false},{"position":21,"method":"CatBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1354.0,"score":0.192,"rank":28.01,"hrank":20.68,"imp":17.279,"train_time":1739.17,"predict_time":0.051,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+39\/-36","imputed":false},{"position":22,"method":"LightGBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1353.0,"score":0.185,"rank":28.07,"hrank":21.83,"imp":17.389,"train_time":892.49,"predict_time":0.346,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+32\/-31","imputed":false},{"position":23,"method":"TabDPT","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2410.18164","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1332.0,"score":0.275,"rank":29.74,"hrank":13.6,"imp":18.023,"train_time":50.33,"predict_time":43.707,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+74\/-67","imputed":false},{"position":24,"method":"xRFM","variant":"Tuned + Ens.","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1330.0,"score":0.203,"rank":29.84,"hrank":18.17,"imp":17.868,"train_time":1126.97,"predict_time":2.94,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+48\/-42","imputed":false},{"position":25,"method":"XGBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1325.0,"score":0.158,"rank":30.28,"hrank":23.33,"imp":17.924,"train_time":884.48,"predict_time":2.367,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+33\/-37","imputed":false},{"position":26,"method":"ModernNCA","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1323.0,"score":0.178,"rank":30.38,"hrank":15.63,"imp":18.056,"train_time":6523.55,"predict_time":0.505,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+40\/-42","imputed":false},{"position":27,"method":"ModernNCA","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1323.0,"score":0.227,"rank":30.39,"hrank":13.02,"imp":18.319,"train_time":6523.55,"predict_time":8.39,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+61\/-56","imputed":false},{"position":28,"method":"CatBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1706.09516","family_symbol":"\ud83c\udf33","verified":true,"elo":1320.0,"score":0.159,"rank":30.6,"hrank":22.44,"imp":18.203,"train_time":8.46,"predict_time":0.046,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+35\/-35","imputed":false},{"position":29,"method":"SAP-RPT-OSS","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2506.10707","family_symbol":"\ud83e\udde0\u26a1","verified":false,"elo":1319.0,"score":0.263,"rank":30.69,"hrank":11.14,"imp":18.839,"train_time":16.23,"predict_time":1.277,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+71\/-57","imputed":false},{"position":30,"method":"XGBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1306.0,"score":0.13,"rank":31.71,"hrank":23.26,"imp":18.133,"train_time":884.48,"predict_time":0.393,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+32\/-40","imputed":false},{"position":31,"method":"TabM","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2410.24210","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1296.0,"score":0.188,"rank":32.54,"hrank":19.64,"imp":18.258,"train_time":9.94,"predict_time":0.445,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+53\/-45","imputed":false},{"position":32,"method":"xRFM","variant":"Tuned","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1282.0,"score":0.157,"rank":33.6,"hrank":20.71,"imp":18.894,"train_time":1126.97,"predict_time":0.163,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+42\/-42","imputed":false},{"position":33,"method":"ChimeraBoost","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1278.0,"score":0.133,"rank":33.92,"hrank":23.84,"imp":19.516,"train_time":1218.87,"predict_time":0.455,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-46","imputed":false},{"position":34,"method":"TorchMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1260.0,"score":0.125,"rank":35.33,"hrank":24.98,"imp":19.124,"train_time":3865.95,"predict_time":2.157,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+40\/-49","imputed":false},{"position":35,"method":"ChimeraBoost","variant":"Tuned","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1260.0,"score":0.11,"rank":35.37,"hrank":25.32,"imp":19.803,"train_time":1218.87,"predict_time":0.036,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+52\/-50","imputed":false},{"position":36,"method":"EBM","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1260.0,"score":0.109,"rank":35.38,"hrank":25.23,"imp":19.954,"train_time":2499.4,"predict_time":0.153,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+42\/-45","imputed":false},{"position":37,"method":"iLTM","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1258.0,"score":0.092,"rank":35.55,"hrank":26.17,"imp":19.245,"train_time":22811.74,"predict_time":161.221,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+37\/-35","imputed":false},{"position":38,"method":"ModernNCA","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.03257","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1237.0,"score":0.097,"rank":37.17,"hrank":21.05,"imp":21.221,"train_time":16.28,"predict_time":0.321,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+47\/-51","imputed":false},{"position":39,"method":"EBM","variant":"Tuned","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1231.0,"score":0.079,"rank":37.67,"hrank":26.59,"imp":20.511,"train_time":2499.4,"predict_time":0.017,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+48\/-44","imputed":false},{"position":40,"method":"RealMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2407.04491","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1221.0,"score":0.079,"rank":38.44,"hrank":26.97,"imp":20.018,"train_time":15.69,"predict_time":4.689,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+39\/-43","imputed":false},{"position":41,"method":"ChimeraBoost","variant":"Default","family":"Tree-based","url":"https:\/\/github.com\/bbstats\/chimeraboost","family_symbol":"\ud83c\udf33","verified":true,"elo":1216.0,"score":0.087,"rank":38.82,"hrank":25.36,"imp":20.941,"train_time":5.49,"predict_time":0.041,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+52\/-58","imputed":false},{"position":42,"method":"FastaiMLP","variant":"Tuned + Ens.","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1214.0,"score":0.124,"rank":38.99,"hrank":22.53,"imp":20.712,"train_time":1679.4,"predict_time":8.069,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+58\/-60","imputed":false},{"position":43,"method":"ExtraTrees","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1211.0,"score":0.109,"rank":39.24,"hrank":24.93,"imp":21.641,"train_time":822.58,"predict_time":1.501,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+69\/-63","imputed":false},{"position":44,"method":"EBM","variant":"Default","family":"Tree-based","url":"https:\/\/www.cs.cornell.edu\/~yinlou\/papers\/lou-kdd13.pdf","family_symbol":"\ud83c\udf33","verified":true,"elo":1203.0,"score":0.074,"rank":39.86,"hrank":25.92,"imp":21.249,"train_time":7.39,"predict_time":0.017,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+48\/-53","imputed":false},{"position":45,"method":"TorchMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1196.0,"score":0.078,"rank":40.43,"hrank":29.54,"imp":20.862,"train_time":3865.95,"predict_time":0.147,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+43\/-49","imputed":false},{"position":46,"method":"ExtraTrees","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1187.0,"score":0.101,"rank":41.1,"hrank":23.0,"imp":22.339,"train_time":822.58,"predict_time":0.214,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+65\/-66","imputed":false},{"position":47,"method":"LightGBM","variant":"Default","family":"Tree-based","url":"https:\/\/papers.nips.cc\/paper_files\/paper\/2017\/hash\/6449f44a102fde848669bdd9eb6b76fa-Abstract.html","family_symbol":"\ud83c\udf33","verified":true,"elo":1166.0,"score":0.043,"rank":42.71,"hrank":35.31,"imp":21.229,"train_time":3.61,"predict_time":0.17,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+43\/-44","imputed":false},{"position":48,"method":"FastaiMLP","variant":"Tuned","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1165.0,"score":0.061,"rank":42.8,"hrank":27.62,"imp":21.847,"train_time":1679.4,"predict_time":0.826,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+51\/-52","imputed":false},{"position":49,"method":"XGBoost","variant":"Default","family":"Tree-based","url":"https:\/\/arxiv.org\/abs\/1603.02754","family_symbol":"\ud83c\udf33","verified":true,"elo":1163.0,"score":0.018,"rank":42.91,"hrank":37.21,"imp":21.244,"train_time":3.29,"predict_time":0.253,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+27\/-37","imputed":false},{"position":50,"method":"RandomForest","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1150.0,"score":0.073,"rank":43.96,"hrank":26.88,"imp":23.216,"train_time":590.04,"predict_time":1.46,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+59\/-70","imputed":false},{"position":51,"method":"TabSTAR","variant":"Tuned","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1132.0,"score":0.178,"rank":45.27,"hrank":12.31,"imp":25.491,"train_time":40848.87,"predict_time":5.606,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+77\/-83","imputed":false},{"position":52,"method":"TabSTAR","variant":"Tuned + Ens.","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1130.0,"score":0.174,"rank":45.38,"hrank":11.81,"imp":25.525,"train_time":40848.87,"predict_time":25.936,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+78\/-84","imputed":false},{"position":53,"method":"RandomForest","variant":"Tuned","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1113.0,"score":0.046,"rank":46.61,"hrank":25.91,"imp":23.948,"train_time":590.04,"predict_time":0.15,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+52\/-68","imputed":false},{"position":54,"method":"PerpetualBooster","variant":"Tuned + Ens.","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1084.0,"score":0.026,"rank":48.72,"hrank":37.31,"imp":28.451,"train_time":342.04,"predict_time":0.895,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+46\/-59","imputed":false},{"position":55,"method":"iLTM","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2511.15941","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1066.0,"score":0.039,"rank":49.88,"hrank":31.58,"imp":24.914,"train_time":524.48,"predict_time":161.813,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+62\/-74","imputed":false},{"position":56,"method":"TorchMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1058.0,"score":0.015,"rank":50.45,"hrank":36.89,"imp":24.761,"train_time":13.69,"predict_time":0.155,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+46\/-50","imputed":false},{"position":57,"method":"PerpetualBooster","variant":"Tuned","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":1048.0,"score":0.014,"rank":51.08,"hrank":37.33,"imp":29.908,"train_time":342.04,"predict_time":0.313,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+50\/-57","imputed":false},{"position":58,"method":"xRFM","variant":"Default","family":"Other","url":"https:\/\/arxiv.org\/abs\/2508.10053","family_symbol":"\u2753","verified":true,"elo":1043.0,"score":0.034,"rank":51.4,"hrank":42.96,"imp":26.639,"train_time":5.45,"predict_time":1.098,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+61\/-86","imputed":false},{"position":59,"method":"TabSTAR","variant":"Default","family":"Foundation Model","url":"https:\/\/arxiv.org\/abs\/2505.18125","family_symbol":"\ud83e\udde0\u26a1","verified":true,"elo":1033.0,"score":0.109,"rank":52.04,"hrank":19.43,"imp":30.36,"train_time":471.66,"predict_time":7.214,"imputed_pct":0.0,"hardware":"GPU","elo_ci":"+79\/-124","imputed":false},{"position":60,"method":"ExtraTrees","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1007\/s10994-006-6226-1","family_symbol":"\ud83c\udf33","verified":true,"elo":1020.0,"score":0.017,"rank":52.89,"hrank":44.87,"imp":27.588,"train_time":4.19,"predict_time":0.566,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+60\/-69","imputed":false},{"position":61,"method":"RandomForest","variant":"Default","family":"Tree-based","url":"https:\/\/link.springer.com\/article\/10.1023\/A:1010933404324","family_symbol":"\ud83c\udf33","verified":true,"elo":1000.0,"score":0.004,"rank":54.07,"hrank":48.1,"imp":28.377,"train_time":0.51,"predict_time":0.089,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+58\/-71","imputed":false},{"position":62,"method":"FastaiMLP","variant":"Default","family":"Neural Network","url":"https:\/\/arxiv.org\/abs\/2003.06505","family_symbol":"\ud83e\udde0\ud83d\udd01","verified":true,"elo":1000.0,"score":0.005,"rank":54.08,"hrank":37.17,"imp":26.856,"train_time":5.48,"predict_time":0.684,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+49\/-62","imputed":false},{"position":63,"method":"KNN","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":1000.0,"score":0.022,"rank":54.08,"hrank":35.68,"imp":29.267,"train_time":111.87,"predict_time":1.214,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+65\/-94","imputed":false},{"position":64,"method":"Linear","variant":"Tuned + Ens.","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":974.0,"score":0.042,"rank":55.53,"hrank":37.13,"imp":33.999,"train_time":521.07,"predict_time":0.646,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+86\/-95","imputed":false},{"position":65,"method":"Linear","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":954.0,"score":0.03,"rank":56.63,"hrank":38.68,"imp":34.411,"train_time":521.07,"predict_time":0.113,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+85\/-99","imputed":false},{"position":66,"method":"PerpetualBooster","variant":"Default","family":"Tree-based","url":"https:\/\/perpetual-ml.com\/","family_symbol":"\ud83c\udf33","verified":true,"elo":947.0,"score":0.0,"rank":57.02,"hrank":41.72,"imp":32.717,"train_time":39.09,"predict_time":0.041,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+41\/-58","imputed":false},{"position":67,"method":"KNN","variant":"Tuned","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":912.0,"score":0.012,"rank":58.72,"hrank":42.52,"imp":33.953,"train_time":111.87,"predict_time":0.111,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+68\/-89","imputed":false},{"position":68,"method":"Linear","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/linear_model.html","family_symbol":"\ud83d\udccf","verified":true,"elo":879.0,"score":0.019,"rank":60.23,"hrank":43.4,"imp":37.25,"train_time":2.59,"predict_time":0.138,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+95\/-118","imputed":false},{"position":69,"method":"KNN","variant":"Default","family":"Baseline","url":"https:\/\/scikit-learn.org\/stable\/modules\/neighbors.html","family_symbol":"\ud83d\udccf","verified":true,"elo":670.0,"score":0.0,"rank":66.58,"hrank":57.89,"imp":47.022,"train_time":0.37,"predict_time":0.033,"imputed_pct":0.0,"hardware":"CPU","elo_ci":"+98\/-134","imputed":false}];
367
-
368
-
369
- const NS = "http://www.w3.org/2000/svg";
370
- // Baseline and Other are one bucket, as in the site's own type legend: they
371
- // already share a color, and each holds only a handful of methods.
372
- const FAM_MERGED = "Baseline / Other";
373
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
374
- // Normalized up front so every later lookup — colors, chips, sorting — sees
375
- // the merged family. Both templates declare POINTS above this block.
376
- for (const p of POINTS) p.family = famOf(p.family);
377
-
378
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
379
- const FAM_VAR = {
380
- "Foundation Model": "var(--fam-foundation)",
381
- "Tree-based": "var(--fam-tree)",
382
- "Neural Network": "var(--fam-nn)",
383
- "Reference Pipeline": "var(--fam-reference)",
384
- [FAM_MERGED]: "var(--fam-baseline)",
385
- };
386
- // The symbol the website shows for each family, so a family chip here reads the
387
- // same as the Type column on the site. Baseline and Other are one bucket, so
388
- // that chip carries both symbols.
389
- const FAM_SYMBOL = {
390
- "Foundation Model": "🧠⚡",
391
- "Tree-based": "🌳",
392
- "Neural Network": "🧠🔁",
393
- "Reference Pipeline": "📊",
394
- [FAM_MERGED]: "📏 ❓",
395
- };
396
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
397
- const VARIANT_VAR = {
398
- "Default": "var(--var-default)",
399
- "Tuned": "var(--var-tuned)",
400
- "Tuned + Ens.": "var(--var-tunedens)",
401
- };
402
-
403
- // A family chip's label: its symbol, its name and how many methods it holds.
404
- function famChipLabel(family, count) {
405
- const symbol = FAM_SYMBOL[family];
406
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
407
- family + ' <span class="count">&times;' + count + "</span>";
408
- }
409
-
410
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
411
- const FAM_INK = {
412
- "Foundation Model": "var(--fam-foundation-ink)",
413
- "Tree-based": "var(--fam-tree-ink)",
414
- "Neural Network": "var(--fam-nn-ink)",
415
- "Reference Pipeline": "var(--fam-reference-ink)",
416
- [FAM_MERGED]: "var(--fam-baseline-ink)",
417
- };
418
-
419
- // Create an SVG element with attributes, optionally appended to `parent`.
420
- function el(name, attrs, parent) {
421
- const node = document.createElementNS(NS, name);
422
- for (const k in attrs) node.setAttribute(k, attrs[k]);
423
- if (parent) parent.appendChild(node);
424
- return node;
425
- }
426
-
427
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
428
- // locale-independent by definition, which is the point: `toLocaleString`
429
- // would follow the *viewer's* browser locale and print 1234,5 for a German
430
- // visitor, disagreeing with the figures and CSVs beside it.
431
- function fmtNum(v, decimals) {
432
- if (v == null || !isFinite(v)) return "—";
433
- return v.toFixed(decimals);
434
- }
435
-
436
- function fmtMetric(metric, v) {
437
- if (v == null || !isFinite(v)) return "—";
438
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
439
- }
440
-
441
- function fmtTime(v) {
442
- if (v >= 100) return fmtNum(v, 0) + " s";
443
- if (v >= 1) return fmtNum(v, 1) + " s";
444
- if (v >= 0.1) return fmtNum(v, 2) + " s";
445
- return fmtNum(v, 3) + " s";
446
- }
447
-
448
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
449
- function niceStep(raw) {
450
- if (!(raw > 0)) return 1;
451
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
452
- for (const m of [1, 2, 2.5, 5, 10]) {
453
- if (mag * m >= raw) return mag * m;
454
- }
455
- return mag * 10;
456
- }
457
-
458
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
459
- // index rather than by accumulation so fractional steps do not drift.
460
- function ticks(min, max, target) {
461
- const step = niceStep((max - min) / target);
462
- const first = Math.ceil(min / step);
463
- const out = [];
464
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
465
- return out.length ? out : [min];
466
- }
467
-
468
- // A hover tooltip anchored inside `box` (which must be position:relative).
469
- function makeTooltip(box) {
470
- const node = box.querySelector(".tooltip");
471
- return {
472
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
473
- move(ev) {
474
- const r = box.getBoundingClientRect();
475
- let tx = ev.clientX - r.left + 14;
476
- const ty = ev.clientY - r.top + 12;
477
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
478
- node.style.left = tx + "px";
479
- node.style.top = ty + "px";
480
- },
481
- hide() { node.style.display = "none"; },
482
- };
483
- }
484
-
485
- // Paper view — white surface, legend + chart only — is the *default*: what a
486
- // reader wants first is the figure, and it is the state worth exporting. The
487
- // controls, chip list and data table are one click away behind "Edit view".
488
- // `afterToggle` re-renders charts whose size is measured from the layout.
489
- // `options.openInPaper` (default true) decides the state the page opens in. A
490
- // chart opens as the figure; the leaderboard table opens with its controls,
491
- // since there the interaction is the point rather than scaffolding around it.
492
- function setUpPaperView(afterToggle, options) {
493
- const opts = options || {};
494
- const root = document.documentElement;
495
- let hostTheme = null; // the embedding page's choice, captured on entry
496
- const btn = document.getElementById("btn-paper");
497
- const embedded = window.parent !== window;
498
-
499
- function setPaper(on) {
500
- document.body.classList.toggle("paper", on);
501
- if (on) {
502
- hostTheme = root.getAttribute("data-theme");
503
- root.setAttribute("data-theme", "light");
504
- } else if (hostTheme) {
505
- root.setAttribute("data-theme", hostTheme);
506
- } else {
507
- root.removeAttribute("data-theme");
508
- }
509
- btn.textContent = on ? "Edit view" : "Paper view";
510
- document.getElementById("exportbar").hidden = !on || embedded;
511
- if (afterToggle) requestAnimationFrame(afterToggle);
512
- postHeight();
513
- }
514
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
515
- // Embedded, the host page owns these controls — they sit beside the panel's
516
- // static-figure toggle and are driven from the outside. Standalone (the
517
- // shareable single file) this page needs its own.
518
- if (embedded) document.querySelector(".viewbar").hidden = true;
519
- window.addEventListener("message", ev => {
520
- const d = ev.data;
521
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
522
- });
523
- // Only standalone: embedded, the host owns the button and would not see the
524
- // key press, so its label would fall out of step with the frame.
525
- if (!embedded) {
526
- document.addEventListener("keydown", ev => {
527
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
528
- });
529
- }
530
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
531
- }
532
-
533
- // --- Figure export ---------------------------------------------------------
534
- // The chart is live SVG, so a file can be built from it directly. Three things
535
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
536
- // nothing outside this document), it has no background or font of its own, and
537
- // the legend is HTML rather than part of the SVG.
538
-
539
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
540
- // cloned and its label re-emitted at the measured position. foreignObject would
541
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
542
- // break the PNG path.
543
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
544
- // once the node leaves this document.
545
- function resolveVars(root, resolve) {
546
- for (const node of [root, ...root.querySelectorAll("*")]) {
547
- for (const attr of ["fill", "stroke"]) {
548
- const value = node.getAttribute(attr);
549
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
550
- }
551
- }
552
- }
553
-
554
- function legendToSvg(container, resolve) {
555
- const base = container.getBoundingClientRect();
556
- const group = document.createElementNS(NS, "g");
557
- let height = 0;
558
- for (const item of container.querySelectorAll(".item")) {
559
- const box = item.getBoundingClientRect();
560
- if (!box.width) continue;
561
- height = Math.max(height, box.bottom - base.top);
562
- let textLeft = box.left - base.left;
563
- const glyph = item.querySelector("svg");
564
- if (glyph) {
565
- const gbox = glyph.getBoundingClientRect();
566
- const wrap = el("g", {
567
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
568
- }, group);
569
- const glyphClone = glyph.cloneNode(true);
570
- resolveVars(glyphClone, resolve);
571
- wrap.appendChild(glyphClone);
572
- textLeft = gbox.right - base.left + 5;
573
- }
574
- const label = item.textContent.trim();
575
- if (!label) continue;
576
- const colored = item.querySelector("[style*='color']");
577
- const text = el("text", {
578
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
579
- fill: resolve(getComputedStyle(colored || item).color),
580
- }, group);
581
- text.textContent = label;
582
- }
583
- return { group, height: Math.ceil(height) };
584
- }
585
-
586
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
587
- // y-axis beside the scrolling plot) still exports as one figure.
588
- function buildExportSvg(parts, legendEl, pad = 10) {
589
- const rootStyle = getComputedStyle(document.documentElement);
590
- const resolve = value => String(value).replace(
591
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
592
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
593
-
594
- let chartW = 0, chartH = 0;
595
- for (const part of parts) {
596
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
597
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
598
- }
599
-
600
- const out = document.createElementNS(NS, "svg");
601
- out.setAttribute("xmlns", NS);
602
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
603
- let top = pad;
604
- const later = []; // built after the width is known
605
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
606
- if (legend && legend.height) {
607
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
608
- later.push(() => out.appendChild(legend.group));
609
- top += legend.height + 8;
610
- }
611
-
612
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
613
- const height = top + chartH + pad;
614
- out.setAttribute("width", Math.ceil(width));
615
- out.setAttribute("height", Math.ceil(height));
616
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
617
- for (const build of later) build();
618
-
619
- for (const part of parts) {
620
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
621
- const clone = part.svg.cloneNode(true);
622
- resolveVars(clone, resolve);
623
- while (clone.firstChild) group.appendChild(clone.firstChild);
624
- }
625
- return out;
626
- }
627
-
628
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
629
- function slugify(text) {
630
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
631
- }
632
-
633
- function downloadUrl(url, filename) {
634
- const a = document.createElement("a");
635
- a.href = url;
636
- a.download = filename;
637
- document.body.appendChild(a);
638
- a.click();
639
- a.remove();
640
- }
641
-
642
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
643
- function rasterize(svg, scale, done, fail) {
644
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
645
- const img = new Image();
646
- img.onload = () => {
647
- const canvas = document.createElement("canvas");
648
- canvas.width = Math.round(width * scale);
649
- canvas.height = Math.round(height * scale);
650
- const ctx = canvas.getContext("2d");
651
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
652
- ctx.drawImage(img, 0, 0);
653
- done(canvas, width, height);
654
- };
655
- img.onerror = fail;
656
- img.src = "data:image/svg+xml;charset=utf-8,"
657
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
658
- }
659
-
660
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
661
- // cost this page its zero-dependency, single-file property. The image is stored
662
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
663
- // in points to the figure's CSS size, so it prints at the size it appears here
664
- // and the pixels land at 96*scale dpi.
665
- async function buildPdf(canvas, cssWidth, cssHeight) {
666
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
667
- const rgb = new Uint8Array((pixels.length / 4) * 3);
668
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
669
- rgb[j] = pixels[i];
670
- rgb[j + 1] = pixels[i + 1];
671
- rgb[j + 2] = pixels[i + 2];
672
- }
673
- const deflated = new Uint8Array(await new Response(
674
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
675
-
676
- const encoder = new TextEncoder();
677
- const chunks = [];
678
- const offsets = [];
679
- let cursor = 0;
680
- const put = data => {
681
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
682
- chunks.push(bytes);
683
- cursor += bytes.length;
684
- };
685
- const object = (id, body, stream) => {
686
- offsets[id] = cursor;
687
- put(`${id} 0 obj\n${body}\n`);
688
- if (stream) {
689
- put("stream\n");
690
- put(stream);
691
- put("\nendstream\n");
692
- }
693
- put("endobj\n");
694
- };
695
-
696
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
697
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
698
- put("%PDF-1.4\n");
699
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
700
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
701
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
702
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
703
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
704
- object(4, "<< /Type /XObject /Subtype /Image "
705
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
706
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
707
- object(5, `<< /Length ${content.length} >>`, content);
708
-
709
- const xref = cursor;
710
- let table = "xref\n0 6\n0000000000 65535 f \n";
711
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
712
- put(table);
713
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
714
- return new Blob(chunks, { type: "application/pdf" });
715
- }
716
-
717
- // Wire up the export buttons; `getParts` is called per click so the file always
718
- // matches what is on screen. Returns a `run(format)` the host page can drive.
719
- function setUpExport(getParts, basename) {
720
- const buttons = {
721
- svg: document.getElementById("btn-svg"),
722
- png: document.getElementById("btn-png"),
723
- pdf: document.getElementById("btn-pdf"),
724
- };
725
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
726
-
727
- // A sandboxed frame has no modals, so a failure is reported on the button.
728
- function complain(format) {
729
- const button = buttons[format];
730
- if (!button) return;
731
- const label = button.textContent;
732
- button.textContent = "failed";
733
- setTimeout(() => { button.textContent = label; }, 2500);
734
- }
735
-
736
- function run(format) {
737
- const svg = figure();
738
- const name = basename();
739
- if (format === "svg") {
740
- downloadUrl("data:image/svg+xml;charset=utf-8,"
741
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
742
- return;
743
- }
744
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
745
- // points so the pixels already land near 200 dpi at print size.
746
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
747
- if (format === "png") {
748
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
749
- } else {
750
- buildPdf(canvas, cssWidth, cssHeight)
751
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
752
- .catch(() => complain("pdf"));
753
- }
754
- }, () => complain(format));
755
- }
756
-
757
- for (const format of Object.keys(buttons)) {
758
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
759
- }
760
- // Embedded, the buttons live in the host's panel header (see main.taExport).
761
- window.addEventListener("message", ev => {
762
- const d = ev.data;
763
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
764
- });
765
- }
766
-
767
- // When embedded, report the content height so the host page can size the
768
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
769
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
770
- // clamped to at least the iframe's current viewport, which turns the
771
- // resize round-trip into a grow-forever feedback loop. The change guard
772
- // stops re-posting once the height settles.
773
- let lastPostedHeight = 0;
774
- function postHeight() {
775
- if (window.parent === window) return;
776
- const height = Math.ceil(document.body.offsetHeight);
777
- if (Math.abs(height - lastPostedHeight) < 3) return;
778
- lastPostedHeight = height;
779
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
780
- }
781
-
782
-
783
- const COLUMNS = CONFIG.columns;
784
- const RANK_KEY = CONFIG.rankKey;
785
- const colByKey = new Map(COLUMNS.map(c => [c.key, c]));
786
- const VARIANTS = CONFIG.variants.filter(v => POINTS.some(p => p.variant === v));
787
-
788
- // How a variant is written out. The short "Tuned + Ens." form is the internal key
789
- // (the charts' colour and marker tables are keyed by it), but nothing on the page
790
- // should show the abbreviation: the row tag uses the spelling the data and the
791
- // app's cross-subset table use, and the toggle spells it out in full.
792
- const VARIANT_TEXT = {
793
- "Default": "default",
794
- "Tuned": "tuned",
795
- "Tuned + Ens.": "tuned + ensembled",
796
- };
797
- const VARIANT_BTN = { "Tuned + Ens.": "Tuned + Ensembled" };
798
- const variantText = v => VARIANT_TEXT[v] || v;
799
- const HAS_IMPUTED = POINTS.some(p => p.imputed);
800
-
801
- const state = {
802
- methods: new Set(POINTS.map(p => p.method)),
803
- variants: new Set(VARIANTS),
804
- columns: new Set(COLUMNS.filter(c => c.on !== false).map(c => c.key)),
805
- imputed: true,
806
- search: "",
807
- sortKey: null,
808
- sortAsc: false,
809
- };
810
-
811
- const tblwrap = document.getElementById("tblwrap");
812
- const chipsBox = document.getElementById("chips");
813
- const famChips = new Map();
814
- const chipByMethod = new Map();
815
-
816
- // ---------- heat shading ----------
817
- // Green (best) through olive to red (worst), matching the website's
818
- // cross-subset overview so a reader moving between them reads one scale.
819
- const RAMP = [[0, [28, 120, 62]], [0.5, [138, 122, 36]], [1, [160, 58, 58]]];
820
- function heatColor(frac) {
821
- const f = Math.max(0, Math.min(1, frac));
822
- for (let i = 0; i < RAMP.length - 1; i++) {
823
- const [f0, c0] = RAMP[i], [f1, c1] = RAMP[i + 1];
824
- if (f <= f1) {
825
- const t = f1 === f0 ? 0 : (f - f0) / (f1 - f0);
826
- const mix = c0.map((v, j) => Math.round(v + t * (c1[j] - v)));
827
- return "rgb(" + mix.join(",") + ")";
828
- }
829
- }
830
- return "rgb(" + RAMP[RAMP.length - 1][1].join(",") + ")";
831
- }
832
- // Runtimes span orders of magnitude; shading them linearly paints every model
833
- // the same green and only the slowest one red, so those normalize in log space.
834
- function scaleOf(col, v) { return col.logScale ? Math.log10(v) : v; }
835
- function bounds(rows, col) {
836
- const values = [];
837
- for (const p of rows) {
838
- const v = p[col.key];
839
- if (v == null || !isFinite(v)) continue;
840
- if (col.logScale && !(v > 0)) continue;
841
- values.push(scaleOf(col, v));
842
- }
843
- if (values.length < 2) return null;
844
- const lo = Math.min(...values), hi = Math.max(...values);
845
- return hi > lo ? [lo, hi] : null;
846
- }
847
-
848
- // ---------- cells ----------
849
- function nameCell(p) {
850
- const ink = FAM_INK[p.family] || "var(--muted)";
851
- let inner = escapeHtml(p.method);
852
- if (p.variant) inner += ' <span class="variant-tag">(' + escapeHtml(variantText(p.variant)) + ")</span>";
853
- if (p.verified) inner += ' <span class="verified" title="Verified implementation">&#10004;&#65039;</span>';
854
- if (p.imputed) {
855
- const pct = isFinite(p.imputed_pct) ? fmtNum(p.imputed_pct, 0) + "% " : "";
856
- inner += ' <span class="imp-mark" title="' + pct + 'imputed">&#8225;</span>';
857
- }
858
- const body = p.url
859
- ? '<a href="' + escapeHtml(p.url) + '" target="_blank" rel="noopener" style="color:' + ink +
860
- ';font-weight:600;">' + inner + '<span class="link-icon">&#8599;</span></a>'
861
- : '<span style="color:' + ink + ';font-weight:600;">' + inner + "</span>";
862
- return '<td class="name" data-export="' + escapeHtml(plainName(p)) + '">' + body + "</td>";
863
- }
864
- function plainName(p) {
865
- return p.method + (p.variant ? " (" + p.variant + ")" : "");
866
- }
867
- function escapeHtml(text) {
868
- return String(text == null ? "" : text).replace(/[&<>"']/g, c =>
869
- ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" })[c]);
870
- }
871
-
872
- // ---------- rows ----------
873
- function visibleRows() {
874
- const term = state.search.trim().toLowerCase();
875
- return POINTS.filter(p => {
876
- if (!state.methods.has(p.method)) return false;
877
- if (p.variant && !state.variants.has(p.variant)) return false;
878
- if (!state.imputed && p.imputed) return false;
879
- if (term && !(p.method + " " + (p.variant || "") + " " + p.family).toLowerCase().includes(term)) return false;
880
- return true;
881
- });
882
- }
883
- function sortRows(rows) {
884
- const out = rows.slice();
885
- if (!state.sortKey) return out.sort((a, b) => a.position - b.position);
886
- const col = colByKey.get(state.sortKey);
887
- const sign = state.sortAsc ? 1 : -1;
888
- const keyOf = p => {
889
- const v = p[state.sortKey];
890
- if (col && col.text) return String(v == null ? "" : v).toLowerCase();
891
- return v == null || !isFinite(v) ? null : v;
892
- };
893
- return out.sort((a, b) => {
894
- const ka = keyOf(a), kb = keyOf(b);
895
- if (ka === null || kb === null) return ka === kb ? 0 : ka === null ? 1 : -1;
896
- return ka < kb ? -sign : ka > kb ? sign : 0;
897
- });
898
- }
899
-
900
- function activeColumns() {
901
- return COLUMNS.filter(c => c.always || state.columns.has(c.key));
902
- }
903
-
904
- function render() {
905
- const rows = sortRows(visibleRows());
906
- const cols = activeColumns();
907
- if (!rows.length) {
908
- tblwrap.innerHTML = '<p class="lbt-empty">Nothing selected — turn a family or variant back on.</p>';
909
- document.getElementById("caption").textContent = "";
910
- postHeight();
911
- return;
912
- }
913
- // No medals here: this table is the full ranking, so the position column
914
- // already says who is where. They belong to the cross-subset overview.
915
- const heat = new Map();
916
- for (const col of cols) if (col.heatmap) heat.set(col.key, bounds(rows, col));
917
-
918
- let html = '<table class="lbt"><thead><tr>';
919
- for (const col of cols) {
920
- const sorted = state.sortKey === col.key
921
- ? ' aria-sort="' + (state.sortAsc ? "ascending" : "descending") + '"' : "";
922
- html += "<th" + sorted + ' data-key="' + col.key + '" title="' + escapeHtml(col.hint || col.label) +
923
- '"><span class="lbl">' + escapeHtml(col.label) + "</span>" +
924
- (col.key === "elo" ? ' <span class="ci">(95% CI)</span>' : "") + "</th>";
925
- }
926
- html += "</tr></thead><tbody>";
927
- for (const p of rows) {
928
- html += "<tr>";
929
- for (const col of cols) {
930
- if (col.key === "position") {
931
- html += '<td class="pos num" data-sort="' + p.position + '">' + p.position + "</td>";
932
- } else if (col.key === "family") {
933
- const mark = FAM_VAR[p.family] || "var(--fam-baseline)";
934
- html += '<td class="type" data-sort="' + escapeHtml(p.family) + '" data-export="' + escapeHtml(p.family) +
935
- '"><span class="pill" style="background:color-mix(in srgb, ' + mark +
936
- ' 13%, transparent);color:' + (FAM_INK[p.family] || "var(--muted)") +
937
- ";border:1px solid color-mix(in srgb, " + mark + ' 40%, transparent);">' +
938
- escapeHtml(p.family_symbol || "") + "</span></td>";
939
- } else if (col.key === "model") {
940
- html += nameCell(p);
941
- } else {
942
- html += valueCell(p, col, heat.get(col.key));
943
- }
944
- }
945
- html += "</tr>";
946
- }
947
- tblwrap.innerHTML = html + "</tbody></table>";
948
- for (const th of tblwrap.querySelectorAll("th")) {
949
- th.addEventListener("click", () => sortBy(th.dataset.key));
950
- }
951
- document.getElementById("caption").textContent =
952
- rows.length + (rows.length === 1 ? " row" : " rows") +
953
- " · click a column header to sort, hover one for what it means" +
954
- (heat.size ? " · green is better, red is worse, per column" : "") +
955
- (HAS_IMPUTED ? " · ‡ marks a partly imputed score" : "");
956
- postHeight();
957
- }
958
-
959
- function valueCell(p, col, span) {
960
- const v = p[col.key];
961
- if (v == null || (typeof v === "number" && !isFinite(v))) return '<td class="na">&ndash;</td>';
962
- if (col.text) {
963
- return '<td data-sort="' + escapeHtml(v) + '" data-export="' + escapeHtml(v) + '">' + escapeHtml(v) + "</td>";
964
- }
965
- let style = "";
966
- if (span) {
967
- let frac = (scaleOf(col, v) - span[0]) / (span[1] - span[0]);
968
- if (!col.lowerBetter) frac = 1 - frac;
969
- style = ' style="background:' + heatColor(frac) + ';color:#f7f7f7;"';
970
- }
971
- let text = fmtNum(v, col.decimals) + (col.suffix || "");
972
- let ci = "";
973
- if (col.key === "elo" && p.elo_ci) {
974
- ci = ' <span class="ci">(' + escapeHtml(p.elo_ci) + ")</span>";
975
- }
976
- return '<td class="num" data-sort="' + v + '"' + (ci ? ' data-ci="' + escapeHtml(p.elo_ci) + '"' : "") +
977
- style + ">" + text + ci + "</td>";
978
- }
979
-
980
- function sortBy(key) {
981
- if (!key) return;
982
- if (state.sortKey === key) state.sortAsc = !state.sortAsc;
983
- else { state.sortKey = key; state.sortAsc = !!(colByKey.get(key) || {}).lowerBetter; }
984
- render();
985
- }
986
-
987
- // ---------- chips (families and their methods) ----------
988
- function familyMembers(fam) {
989
- return [...new Set(POINTS.filter(p => p.family === fam).map(p => p.method))];
990
- }
991
- function methodRank(name) {
992
- const col = colByKey.get(RANK_KEY);
993
- const values = POINTS.filter(p => p.method === name && isFinite(p[RANK_KEY])).map(p => p[RANK_KEY]);
994
- if (!values.length) return Infinity;
995
- return col.lowerBetter ? Math.min(...values) : -Math.max(...values);
996
- }
997
- function buildChips() {
998
- const head = document.createElement("div");
999
- head.className = "chips-head";
1000
- head.textContent = "Models shown — click to remove, click a family to toggle the whole group";
1001
- chipsBox.appendChild(head);
1002
- for (const fam of FAM_ORDER) {
1003
- const members = familyMembers(fam);
1004
- if (!members.length) continue;
1005
- members.sort((a, b) => methodRank(a) - methodRank(b));
1006
- const row = document.createElement("div");
1007
- row.className = "chiprow";
1008
- const famBtn = document.createElement("button");
1009
- famBtn.className = "famchip";
1010
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1011
- famBtn.innerHTML = famChipLabel(fam, members.length);
1012
- famBtn.title = "Toggle all " + members.length + " " + fam + " models";
1013
- famBtn.addEventListener("click", () => toggleFamily(fam));
1014
- row.appendChild(famBtn);
1015
- famChips.set(fam, famBtn);
1016
- const set = document.createElement("div");
1017
- set.className = "chipset";
1018
- for (const name of members) {
1019
- const imputed = POINTS.some(p => p.method === name && p.imputed);
1020
- const b = document.createElement("button");
1021
- b.className = "chip";
1022
- b.style.setProperty("--fam", FAM_VAR[fam]);
1023
- b.innerHTML = '<span class="dot"></span><span>' + escapeHtml(name) + "</span>" +
1024
- (imputed ? '<span class="imp-mark">&#8225;</span>' : "");
1025
- b.title = name + (imputed ? " — partially imputed" : "");
1026
- b.addEventListener("click", () => toggleMethod(name));
1027
- set.appendChild(b);
1028
- chipByMethod.set(name, b);
1029
- }
1030
- row.appendChild(set);
1031
- chipsBox.appendChild(row);
1032
- }
1033
- }
1034
- function syncChips() {
1035
- for (const [name, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.methods.has(name)));
1036
- for (const [fam, b] of famChips) {
1037
- b.setAttribute("aria-pressed", String(familyMembers(fam).every(m => state.methods.has(m))));
1038
- }
1039
- }
1040
- function toggleMethod(name) {
1041
- if (state.methods.has(name)) state.methods.delete(name); else state.methods.add(name);
1042
- syncChips();
1043
- render();
1044
- }
1045
- function toggleFamily(fam) {
1046
- const members = familyMembers(fam);
1047
- const allOn = members.every(m => state.methods.has(m));
1048
- for (const m of members) { if (allOn) state.methods.delete(m); else state.methods.add(m); }
1049
- syncChips();
1050
- render();
1051
- }
1052
-
1053
- // ---------- variant / imputed / column toggles ----------
1054
- const variantBtns = new Map();
1055
- function buildVariantBtns() {
1056
- const box = document.getElementById("variant-btns");
1057
- for (const v of VARIANTS) {
1058
- const b = document.createElement("button");
1059
- b.className = "btn toggle";
1060
- b.innerHTML = '<span class="swatch"></span>' + escapeHtml(VARIANT_BTN[v] || v);
1061
- b.style.setProperty("--fam", VARIANT_VAR[v] || "var(--accent)");
1062
- b.title = "Show or hide the " + variantText(v) + " results";
1063
- b.addEventListener("click", () => {
1064
- if (state.variants.has(v)) state.variants.delete(v); else state.variants.add(v);
1065
- syncVariantBtns();
1066
- render();
1067
- });
1068
- box.appendChild(b);
1069
- variantBtns.set(v, b);
1070
- }
1071
- }
1072
- function syncVariantBtns() {
1073
- for (const [v, b] of variantBtns) b.setAttribute("aria-pressed", String(state.variants.has(v)));
1074
- const imp = document.getElementById("btn-imputed");
1075
- imp.hidden = !HAS_IMPUTED;
1076
- imp.className = "btn toggle";
1077
- imp.innerHTML = '<span class="swatch"></span>&#8225; Imputed';
1078
- imp.setAttribute("aria-pressed", String(state.imputed));
1079
- imp.style.setProperty("--fam", "var(--muted)");
1080
- }
1081
- function buildColumnBtns() {
1082
- const box = document.getElementById("col-btns");
1083
- for (const col of COLUMNS) {
1084
- if (col.always) continue;
1085
- const b = document.createElement("button");
1086
- b.className = "btn toggle";
1087
- b.textContent = col.short || col.label;
1088
- b.title = "Show or hide " + col.label;
1089
- b.style.setProperty("--fam", "var(--accent)");
1090
- b.addEventListener("click", () => {
1091
- if (state.columns.has(col.key)) state.columns.delete(col.key); else state.columns.add(col.key);
1092
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1093
- render();
1094
- });
1095
- b.setAttribute("aria-pressed", String(state.columns.has(col.key)));
1096
- box.appendChild(b);
1097
- }
1098
- }
1099
-
1100
- // ---------- CSV ----------
1101
- // Exports what the reader is looking at: the rows the filters left, the
1102
- // columns they picked, in the order they sorted.
1103
- function downloadCsv() {
1104
- const cols = activeColumns();
1105
- const rows = sortRows(visibleRows());
1106
- const quote = v => '"' + String(v == null ? "" : v).replace(/"/g, '""') + '"';
1107
- const head = [];
1108
- for (const col of cols) {
1109
- head.push(col.label);
1110
- if (col.key === "elo" && rows.some(p => p.elo_ci)) head.push("Elo 95% CI");
1111
- }
1112
- const lines = [head.map(quote).join(",")];
1113
- for (const p of rows) {
1114
- const values = [];
1115
- for (const col of cols) {
1116
- if (col.key === "model") values.push(plainName(p));
1117
- else values.push(p[col.key]);
1118
- if (col.key === "elo" && rows.some(q => q.elo_ci)) values.push(p.elo_ci || "");
1119
- }
1120
- lines.push(values.map(quote).join(","));
1121
- }
1122
- const blob = new Blob([lines.join("\n") + "\n"], { type: "text/csv;charset=utf-8" });
1123
- const url = URL.createObjectURL(blob);
1124
- downloadUrl(url, slugify(CONFIG.title || document.title || "leaderboard") + ".csv");
1125
- URL.revokeObjectURL(url);
1126
- }
1127
-
1128
- // ---------- boot ----------
1129
- if (CONFIG.title) document.getElementById("title").textContent = CONFIG.title;
1130
- document.getElementById("search").addEventListener("input", ev => {
1131
- state.search = ev.target.value;
1132
- render();
1133
- });
1134
- document.getElementById("btn-imputed").addEventListener("click", () => {
1135
- state.imputed = !state.imputed;
1136
- syncVariantBtns();
1137
- render();
1138
- });
1139
- document.getElementById("btn-csv").addEventListener("click", downloadCsv);
1140
- // Embedded, the host page puts its own CSV button in the panel header (next to
1141
- // the title) and asks for the download over postMessage, the same way it drives
1142
- // the figure exports. Standalone, this page needs its own button.
1143
- if (window.parent !== window) {
1144
- document.getElementById("btn-csv").hidden = true;
1145
- window.addEventListener("message", ev => {
1146
- if (ev.data && ev.data.type === "tabarena-leaderboard-csv") downloadCsv();
1147
- });
1148
- }
1149
- // Opens with the controls: the filters are the point of this page, not chrome
1150
- // around a figure.
1151
- setUpPaperView(render, { openInPaper: false });
1152
- buildVariantBtns();
1153
- buildColumnBtns();
1154
- buildChips();
1155
- syncChips();
1156
- syncVariantBtns();
1157
- render();
1158
- window.addEventListener("resize", postHeight);
1159
- })();
1160
- </script>
1161
- </body>
1162
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/n_datasets_36 DELETED
File without changes
data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_explorer.html DELETED
@@ -1,1230 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>TabArena Pareto explorer</title>
7
- <style>
8
- :root {
9
- --fam-foundation: #b07cf0;
10
- --fam-nn: #5aa9e6;
11
- --fam-tree: #5cb85c;
12
- --fam-reference: #f0a35a;
13
- --fam-baseline: #9e9e9e;
14
- --fam-other: #9e9e9e;
15
- --paper: #ffffff;
16
- --card: #ffffff;
17
- --ink: #14161a;
18
- --muted: #6d6c65;
19
- --line: #e4e3db;
20
- --accent: #2a78d6;
21
- --chip-bg: #f2f1ec;
22
- --pt-muted: #b9b8b1;
23
- /* The same family hues as *text*. The mark colors are tuned for fills and
24
- drop below readable contrast as small labels on the light surface, so
25
- light mode darkens them; dark mode reuses the mark colors as-is. */
26
- --fam-foundation-ink: #7d3fc2;
27
- --fam-nn-ink: #1c6fa8;
28
- --fam-tree-ink: #2f7d32;
29
- --fam-reference-ink: #a4600f;
30
- --fam-baseline-ink: #5f5f5f;
31
- --fam-other-ink: #5f5f5f;
32
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
33
- is the paper view's surface, so it uses the *static figures' own* seaborn
34
- pastels — figures exported from here drop straight into a paper beside
35
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
36
- ΔE, well inside the band that needs secondary encoding, which here is the
37
- fixed concentric bar widths plus the legend and the data table. Dark mode
38
- (the website) keeps the stepped, better-separated version below. */
39
- --var-default: #a1c9f4;
40
- --var-tuned: #ffb482;
41
- --var-tunedens: #8de5a1;
42
- --optimal: #228b22;
43
- --tooltip-bg: #14161a;
44
- --tooltip-ink: #fbfbf9;
45
- color-scheme: light;
46
- }
47
- @media (prefers-color-scheme: dark) {
48
- :root {
49
- --paper: #131316;
50
- --card: #1b1b1f;
51
- --ink: #f0efea;
52
- --muted: #9b9a92;
53
- --line: #2e2e33;
54
- --accent: #3987e5;
55
- --chip-bg: #232327;
56
- --pt-muted: #55555c;
57
- --fam-foundation-ink: var(--fam-foundation);
58
- --fam-nn-ink: var(--fam-nn);
59
- --fam-tree-ink: var(--fam-tree);
60
- --fam-reference-ink: var(--fam-reference);
61
- --fam-baseline-ink: var(--fam-baseline);
62
- --fam-other-ink: var(--fam-other);
63
- --var-default: #4386d5;
64
- --var-tuned: #c05f38;
65
- --var-tunedens: #289972;
66
- --optimal: #2ea043;
67
- --tooltip-bg: #f0efea;
68
- --tooltip-ink: #14161a;
69
- color-scheme: dark;
70
- }
71
- }
72
- :root[data-theme="dark"] {
73
- --paper: #131316;
74
- --card: #1b1b1f;
75
- --ink: #f0efea;
76
- --muted: #9b9a92;
77
- --line: #2e2e33;
78
- --accent: #3987e5;
79
- --chip-bg: #232327;
80
- --pt-muted: #55555c;
81
- --fam-foundation-ink: var(--fam-foundation);
82
- --fam-nn-ink: var(--fam-nn);
83
- --fam-tree-ink: var(--fam-tree);
84
- --fam-reference-ink: var(--fam-reference);
85
- --fam-baseline-ink: var(--fam-baseline);
86
- --fam-other-ink: var(--fam-other);
87
- --var-default: #4386d5;
88
- --var-tuned: #c05f38;
89
- --var-tunedens: #289972;
90
- --optimal: #2ea043;
91
- --tooltip-bg: #f0efea;
92
- --tooltip-ink: #14161a;
93
- color-scheme: dark;
94
- }
95
- :root[data-theme="light"] {
96
- --paper: #ffffff;
97
- --card: #ffffff;
98
- --ink: #14161a;
99
- --muted: #6d6c65;
100
- --line: #e4e3db;
101
- --accent: #2a78d6;
102
- --chip-bg: #f2f1ec;
103
- --pt-muted: #b9b8b1;
104
- /* The same family hues as *text*. The mark colors are tuned for fills and
105
- drop below readable contrast as small labels on the light surface, so
106
- light mode darkens them; dark mode reuses the mark colors as-is. */
107
- --fam-foundation-ink: #7d3fc2;
108
- --fam-nn-ink: #1c6fa8;
109
- --fam-tree-ink: #2f7d32;
110
- --fam-reference-ink: #a4600f;
111
- --fam-baseline-ink: #5f5f5f;
112
- --fam-other-ink: #5f5f5f;
113
- /* Tuning-variant series (default / tuned / tuned + ensembled). Light mode
114
- is the paper view's surface, so it uses the *static figures' own* seaborn
115
- pastels — figures exported from here drop straight into a paper beside
116
- them. The cost is colorblind separation: green vs. orange is 4.0 deutan
117
- ΔE, well inside the band that needs secondary encoding, which here is the
118
- fixed concentric bar widths plus the legend and the data table. Dark mode
119
- (the website) keeps the stepped, better-separated version below. */
120
- --var-default: #a1c9f4;
121
- --var-tuned: #ffb482;
122
- --var-tunedens: #8de5a1;
123
- --optimal: #228b22;
124
- --tooltip-bg: #14161a;
125
- --tooltip-ink: #fbfbf9;
126
- color-scheme: light;
127
- }
128
- html, body { margin: 0; background: var(--paper); }
129
- /* The colour emoji fonts are named *before* the generic `sans-serif`. A generic
130
- family matches every character through the browser's own fallback chain, so
131
- anything listed after it is unreachable — and that fallback resolves emoji to
132
- a monochrome font on Linux, which flattens the family symbols on the chips.
133
- Latin glyphs are unaffected: the emoji fonts carry none. */
134
- body {
135
- color: var(--ink);
136
- font-family: system-ui, -apple-system, "Segoe UI",
137
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
138
- line-height: 1.5;
139
- padding: 10px 12px 14px;
140
- }
141
- /* The [hidden] attribute must beat author display rules (e.g. the
142
- inline-flex on .metricpick), else hidden controls render empty. */
143
- [hidden] { display: none !important; }
144
-
145
- .explorer-title { font-size: 15px; font-weight: 650; margin: 0 0 8px; }
146
- .controls { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin-bottom: 10px; }
147
- .controls .hint { font-size: 12.5px; color: var(--muted); }
148
- .btnrow { display: flex; gap: 6px; flex-wrap: wrap; }
149
- .btn {
150
- font: 600 12.5px/1 system-ui, sans-serif; color: var(--ink);
151
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
152
- padding: 6px 11px; cursor: pointer;
153
- }
154
- .btn:hover { border-color: var(--muted); }
155
- .btn:focus-visible, .chip:focus-visible, .famchip:focus-visible, select:focus-visible {
156
- outline: 2px solid var(--accent); outline-offset: 2px;
157
- }
158
- .metricpick { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--muted); }
159
- .metricpick select {
160
- font: 600 12.5px/1.2 system-ui, sans-serif; color: var(--ink);
161
- background: var(--chip-bg); border: 1px solid var(--line); border-radius: 7px;
162
- padding: 5px 7px; cursor: pointer;
163
- }
164
-
165
- .chips { display: flex; flex-direction: column; gap: 9px; }
166
- /* One block per family: the family toggle on top, its chips wrapping below. */
167
- .chiprow { display: flex; flex-direction: column; align-items: flex-start; gap: 5px; }
168
- .famchip {
169
- display: inline-flex; align-items: center; gap: 6px;
170
- font: 650 10.5px/1.3 system-ui, sans-serif; letter-spacing: 0.06em; text-transform: uppercase;
171
- color: var(--muted); background: var(--chip-bg); border: 1px dashed var(--line);
172
- border-radius: 999px; padding: 5px 10px; cursor: pointer;
173
- }
174
- .famchip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--fam); flex: none; }
175
- .famchip .count { font-weight: 500; letter-spacing: 0; opacity: 0.75; }
176
- /* The family symbol sits at text size, not the chip's small-caps size. */
177
- .famchip .sym { font-size: 1.05em; letter-spacing: 0; }
178
- .famchip:hover { border-color: var(--fam); color: var(--ink); }
179
- .famchip[aria-pressed="true"] {
180
- border: 1px solid var(--fam);
181
- background: color-mix(in srgb, var(--fam) 13%, transparent);
182
- color: var(--ink);
183
- }
184
- /* A toggle button that carries its own colour: off is faded with a neutral
185
- border, on takes the colour as border and tint. Opt-in via `.toggle` so the
186
- older explorers, which fade their variant buttons with inline styles, are
187
- unaffected. */
188
- .btn.toggle[aria-pressed] { opacity: 0.5; }
189
- .btn.toggle[aria-pressed="true"] {
190
- opacity: 1;
191
- border-color: var(--fam);
192
- background: color-mix(in srgb, var(--fam) 18%, var(--chip-bg));
193
- }
194
- .btn.toggle .swatch {
195
- display: inline-block; width: 8px; height: 8px; border-radius: 50%;
196
- background: var(--fam); margin-right: 6px; vertical-align: middle;
197
- }
198
- .btn.toggle[aria-pressed="false"] .swatch { background: var(--pt-muted); }
199
- .grouplabel { font-size: 12.5px; font-weight: 600; color: var(--muted); }
200
-
201
- .chipset { display: flex; flex-wrap: wrap; gap: 4px; }
202
- .chip {
203
- display: inline-flex; align-items: center; gap: 5px;
204
- font: 500 12.5px/1 system-ui, sans-serif; color: var(--ink);
205
- background: none; border: 1px solid var(--line); border-radius: 999px;
206
- padding: 5px 10px 5px 8px; cursor: pointer;
207
- }
208
- .chip .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--pt-muted); flex: none; }
209
- .chip .imp-mark { color: var(--muted); font-weight: 700; margin-left: -2px; }
210
- .chip[aria-pressed="true"] { border-color: var(--fam); background: color-mix(in srgb, var(--fam) 13%, transparent); font-weight: 650; }
211
- .chip[aria-pressed="true"] .dot { background: var(--fam); }
212
- .chip:hover { border-color: var(--muted); }
213
-
214
- .legendstrip {
215
- display: flex; flex-wrap: wrap; gap: 5px 16px; align-items: center;
216
- font-size: 12.5px; color: var(--muted); padding: 2px 2px 8px;
217
- }
218
- .legendstrip .item { display: inline-flex; align-items: center; gap: 6px; }
219
-
220
- .tooltip {
221
- position: absolute; pointer-events: none; display: none;
222
- background: var(--tooltip-bg); color: var(--tooltip-ink);
223
- border-radius: 8px; padding: 8px 11px; font-size: 12px; line-height: 1.45;
224
- max-width: 260px; z-index: 5; font-variant-numeric: tabular-nums;
225
- box-shadow: 0 4px 14px rgba(0,0,0,0.25);
226
- }
227
- .tooltip .t-name { font-weight: 700; font-size: 12.5px; }
228
- .tooltip .t-var { opacity: 0.75; }
229
- .tooltip .t-imp { opacity: 0.85; font-style: italic; }
230
-
231
- details.datatable { margin-top: 8px; font-size: 12.5px; }
232
- details.datatable summary { cursor: pointer; color: var(--muted); font-weight: 600; }
233
- details.datatable .tblwrap { overflow-x: auto; margin-top: 8px; }
234
- details.datatable table { border-collapse: collapse; font-variant-numeric: tabular-nums; min-width: 560px; }
235
- details.datatable th, details.datatable td {
236
- text-align: left; padding: 3px 12px 3px 0; border-bottom: 1px solid var(--line);
237
- }
238
- details.datatable th { font-size: 11px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted); }
239
-
240
- svg text {
241
- font-family: system-ui, -apple-system, "Segoe UI",
242
- "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
243
- }
244
-
245
- @media (prefers-reduced-motion: no-preference) {
246
- .chip, .btn, .famchip { transition: border-color 120ms ease, background-color 120ms ease; }
247
- }
248
-
249
- /* --- Paper view -----------------------------------------------------------
250
- A figure-ready state for slides and papers: white surface, and only the
251
- things needed to read the plot — the caption, the legend and the chart.
252
- The controls, the chip list and the data table are interactive scaffolding,
253
- not part of the figure. Entered via the "Paper view" button (which stamps
254
- data-theme="light" on the root, see the scopes above). */
255
- /* One toggle, in the same place in both states and never hidden — an exit
256
- tucked into a corner of the figure was easy to miss. */
257
- .viewbar { display: flex; align-items: center; gap: 10px; margin: 0 0 9px; }
258
- body.paper .controls,
259
- body.paper .chips,
260
- body.paper .sidebox,
261
- body.paper details.datatable { display: none !important; }
262
- body.paper { padding: 14px 18px 18px; }
263
- /* Export controls, revealed with the paper view. */
264
- .exportbar { display: flex; align-items: center; gap: 8px; margin: 0 0 10px; }
265
- .exportbar .hint { font-size: 12.5px; font-weight: 600; color: var(--muted); }
266
-
267
-
268
- /* Two-column layout: controls + chips in a side panel, chart beside it.
269
- ``chips-right`` mirrors the columns. Wraps to stacked when narrow. */
270
- .explorer-grid { display: flex; gap: 18px; align-items: flex-start; }
271
- .explorer-grid.chips-right { flex-direction: row-reverse; }
272
- .sidebox { flex: 0 0 330px; min-width: 250px; display: flex; flex-direction: column; gap: 10px; }
273
- .mainbox { flex: 1 1 auto; min-width: 0; }
274
- @media (max-width: 860px) {
275
- .explorer-grid { flex-wrap: wrap; }
276
- .sidebox { flex: 1 1 100%; }
277
- }
278
-
279
- .legendstrip .legendbreak { flex-basis: 100%; height: 0; }
280
- .chartbox { position: relative; }
281
- /* The chart is sized in device pixels by render() rather than scaled from a
282
- viewBox: scaling stretched the type along with the plot, and a viewBox tall
283
- enough to read made the whole panel own the screen. */
284
- .chartbox svg { display: block; }
285
- /* Chips scroll within the column so the panel height follows the chart. */
286
- .sidebox .chips { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--pt-muted) transparent; }
287
- .sidebox .chips::-webkit-scrollbar { width: 9px; }
288
- .sidebox .chips::-webkit-scrollbar-thumb {
289
- background: var(--pt-muted); border-radius: 8px; border: 3px solid transparent; background-clip: content-box;
290
- }
291
- </style>
292
- </head>
293
- <body>
294
- <div class="viewbar">
295
- <button class="btn" id="btn-paper" title="White background, chart and legend only — for slides and papers">Paper view</button>
296
- </div>
297
- <p class="explorer-title" id="title"></p>
298
- <div class="explorer-grid" id="grid">
299
- <div class="sidebox">
300
- <div class="controls">
301
- <label class="metricpick" id="metricpick" hidden>Y-axis
302
- <select id="metric-select"></select>
303
- </label>
304
- <div class="btnrow">
305
- <button class="btn" id="btn-front">Pareto front</button>
306
- <button class="btn" id="btn-all">All</button>
307
- <button class="btn" id="btn-none">Clear</button>
308
- </div>
309
- <span class="hint">Click methods or family buttons to highlight &middot; hover points for details</span>
310
- </div>
311
- <div class="chips" id="chips"></div>
312
- </div>
313
- <div class="mainbox">
314
- <div class="exportbar" id="exportbar" hidden>
315
- <span class="hint">Export figure</span>
316
- <button class="btn" id="btn-svg" title="Download as SVG — vector, keeps text selectable">SVG</button>
317
- <button class="btn" id="btn-pdf" title="Download as a one-page PDF">PDF</button>
318
- <button class="btn" id="btn-png" title="Download as PNG at 3x scale">PNG</button>
319
- </div>
320
- <!-- Legend above the chart so readers decode the marks before the data. -->
321
- <div class="legendstrip" id="legendstrip"></div>
322
- <div class="chartbox" id="chartbox">
323
- <svg id="chart" role="img" aria-label="Pareto front explorer"></svg>
324
- <div class="tooltip"></div>
325
- </div>
326
- </div>
327
- </div>
328
- <details class="datatable">
329
- <summary>Data table</summary>
330
- <div class="tblwrap" id="tblwrap"></div>
331
- </details>
332
-
333
- <script>
334
- (function () {
335
- "use strict";
336
- const CONFIG = {"mode": "scatter", "title": null, "metrics": [{"key": "imp", "label": "Improvability (%)", "axisLabel": "Improvability (%) \u2014 lower is better", "lowerBetter": true, "fromZero": true, "decimals": 1, "suffix": "%"}, {"key": "elo", "label": "Elo", "axisLabel": "Elo \u2014 higher is better", "lowerBetter": false, "fromZero": false, "decimals": 0, "suffix": ""}], "xAxes": [{"key": "x_infer", "label": "Inference time", "axisLabel": "Inference time per 1K samples (s), median \u2014 log scale", "short": "Inference (s/1K, median)"}], "chipsSide": "left"};
337
- const POINTS = [{"method":"CatBoost","family":"Tree-based","x_infer":0.0455896074,"imp":18.2032871809,"elo":1320.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"CatBoost","family":"Tree-based","x_infer":0.0508532668,"imp":17.2788594488,"elo":1354.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"CatBoost","family":"Tree-based","x_infer":0.3960267603,"imp":17.0406428568,"elo":1367.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0413889487,"imp":20.9409846453,"elo":1216.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.0359833093,"imp":19.8033433908,"elo":1259.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ChimeraBoost","family":"Tree-based","x_infer":0.454903865,"imp":19.5160246588,"elo":1278.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"EBM","family":"Tree-based","x_infer":0.0171560587,"imp":21.2493491202,"elo":1203.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"EBM","family":"Tree-based","x_infer":0.0172938338,"imp":20.510545928,"elo":1230.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"EBM","family":"Tree-based","x_infer":0.1529693961,"imp":19.9544651059,"elo":1259.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.5663988088,"imp":27.5877514986,"elo":1019.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ExtraTrees","family":"Tree-based","x_infer":0.2136923717,"imp":22.3391093508,"elo":1187.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ExtraTrees","family":"Tree-based","x_infer":1.5011279394,"imp":21.6412157659,"elo":1210.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.6842938244,"imp":26.8558318039,"elo":999.9,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"FastaiMLP","family":"Neural Network","x_infer":0.82578692,"imp":21.847378167,"elo":1164.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"FastaiMLP","family":"Neural Network","x_infer":8.0688374767,"imp":20.7123810494,"elo":1214.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LightGBM","family":"Tree-based","x_infer":0.1695753133,"imp":21.2294827321,"elo":1166.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"LightGBM","family":"Tree-based","x_infer":0.3458375858,"imp":17.3893988993,"elo":1353.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"LightGBM","family":"Tree-based","x_infer":2.5704534161,"imp":16.9366876587,"elo":1389.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"LimiX","family":"Foundation Model","x_infer":6.8284826783,"imp":15.1548850837,"elo":1405.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.320600689,"imp":21.2208702147,"elo":1237.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"ModernNCA","family":"Neural Network","x_infer":0.5053503954,"imp":18.0561303362,"elo":1323.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"ModernNCA","family":"Neural Network","x_infer":8.3903226253,"imp":18.3187609832,"elo":1323.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.0406570787,"imp":32.7166050918,"elo":946.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.3125464363,"imp":29.9076789954,"elo":1048.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"PerpetualBooster","family":"Tree-based","x_infer":0.8948102984,"imp":28.4508866176,"elo":1083.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RandomForest","family":"Tree-based","x_infer":0.0885648768,"imp":28.3765267033,"elo":1000.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RandomForest","family":"Tree-based","x_infer":0.1503007896,"imp":23.9480322533,"elo":1113.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RandomForest","family":"Tree-based","x_infer":1.4602306271,"imp":23.2155091609,"elo":1149.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealMLP","family":"Neural Network","x_infer":4.6888676463,"imp":20.0184669583,"elo":1221.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealMLP","family":"Neural Network","x_infer":1.7776750936,"imp":15.9097834653,"elo":1399.9,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealMLP","family":"Neural Network","x_infer":21.9024962791,"imp":14.5333602982,"elo":1479.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":0.6362272808,"imp":12.3200887376,"elo":1541.9,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":1.1425914845,"imp":11.9071180723,"elo":1555.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"RealTabPFN-2.5","family":"Foundation Model","x_infer":8.0475948482,"imp":11.1975772448,"elo":1595.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"SAP-RPT-OSS","family":"Foundation Model","x_infer":1.2773508677,"imp":18.8388371702,"elo":1319.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":43.7074010359,"imp":18.0230648308,"elo":1331.6,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabDPT","family":"Foundation Model","x_infer":28.3513403832,"imp":15.883723432,"elo":1415.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabDPT","family":"Foundation Model","x_infer":218.4533640735,"imp":15.2464708261,"elo":1448.0,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabDPT-Turbo","family":"Foundation Model","x_infer":0.1922706752,"imp":15.3295501945,"elo":1448.5,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabFM","family":"Foundation Model","x_infer":5.3578381333,"imp":4.6543552541,"elo":1743.7,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabICLv2","family":"Foundation Model","x_infer":0.1520731745,"imp":11.5604085785,"elo":1572.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.444937792,"imp":18.2578343688,"elo":1295.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabM","family":"Neural Network","x_infer":0.4361574183,"imp":16.5609732873,"elo":1373.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabM","family":"Neural Network","x_infer":3.8381409686,"imp":15.6750950369,"elo":1429.1,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabPFN-2.6","family":"Foundation Model","x_infer":0.548288662,"imp":11.9607415625,"elo":1599.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabPFN-3","family":"Foundation Model","x_infer":0.4158216639,"imp":10.7658050052,"elo":1636.0,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":7.2137264423,"imp":30.3598793602,"elo":1033.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TabSTAR","family":"Foundation Model","x_infer":5.6062080426,"imp":25.4914369812,"elo":1131.8,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TabSTAR","family":"Foundation Model","x_infer":25.9362072534,"imp":25.5248178568,"elo":1130.2,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"TabSwift","family":"Foundation Model","x_infer":0.0579126899,"imp":17.2241080479,"elo":1363.2,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.1550906392,"imp":24.7609027784,"elo":1057.8,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"TorchMLP","family":"Neural Network","x_infer":0.1474394112,"imp":20.8615927184,"elo":1195.6,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"TorchMLP","family":"Neural Network","x_infer":2.1570271396,"imp":19.1243302341,"elo":1260.5,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"XGBoost","family":"Tree-based","x_infer":0.2525411892,"imp":21.2442258572,"elo":1163.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"XGBoost","family":"Tree-based","x_infer":0.3929683731,"imp":18.1328413794,"elo":1306.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"XGBoost","family":"Tree-based","x_infer":2.3674863949,"imp":17.9237428347,"elo":1324.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"iLTM","family":"Foundation Model","x_infer":161.8134383214,"imp":24.9136024932,"elo":1066.4,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"iLTM","family":"Foundation Model","x_infer":161.2206474003,"imp":19.245205487,"elo":1257.7,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"iLTM","family":"Foundation Model","x_infer":841.4458907481,"imp":17.4214786951,"elo":1361.3,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."},{"method":"xRFM","family":"Other","x_infer":1.0976028857,"imp":26.6387104203,"elo":1043.3,"imputed":false,"imputed_pct":0.0,"variant":"Default"},{"method":"xRFM","family":"Other","x_infer":0.1632446807,"imp":18.8938182398,"elo":1282.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned"},{"method":"xRFM","family":"Other","x_infer":2.9395549707,"imp":17.8683612217,"elo":1330.4,"imputed":false,"imputed_pct":0.0,"variant":"Tuned + Ens."}];
338
-
339
-
340
- const NS = "http://www.w3.org/2000/svg";
341
- // Baseline and Other are one bucket, as in the site's own type legend: they
342
- // already share a color, and each holds only a handful of methods.
343
- const FAM_MERGED = "Baseline / Other";
344
- const famOf = (family) => (family === "Baseline" || family === "Other" ? FAM_MERGED : family);
345
- // Normalized up front so every later lookup — colors, chips, sorting — sees
346
- // the merged family. Both templates declare POINTS above this block.
347
- for (const p of POINTS) p.family = famOf(p.family);
348
-
349
- const FAM_ORDER = ["Foundation Model", "Tree-based", "Neural Network", "Reference Pipeline", FAM_MERGED];
350
- const FAM_VAR = {
351
- "Foundation Model": "var(--fam-foundation)",
352
- "Tree-based": "var(--fam-tree)",
353
- "Neural Network": "var(--fam-nn)",
354
- "Reference Pipeline": "var(--fam-reference)",
355
- [FAM_MERGED]: "var(--fam-baseline)",
356
- };
357
- // The symbol the website shows for each family, so a family chip here reads the
358
- // same as the Type column on the site. Baseline and Other are one bucket, so
359
- // that chip carries both symbols.
360
- const FAM_SYMBOL = {
361
- "Foundation Model": "🧠⚡",
362
- "Tree-based": "🌳",
363
- "Neural Network": "🧠🔁",
364
- "Reference Pipeline": "📊",
365
- [FAM_MERGED]: "📏 ❓",
366
- };
367
- // Tuning-variant colours, matching the --var-* tokens the charts plot with.
368
- const VARIANT_VAR = {
369
- "Default": "var(--var-default)",
370
- "Tuned": "var(--var-tuned)",
371
- "Tuned + Ens.": "var(--var-tunedens)",
372
- };
373
-
374
- // A family chip's label: its symbol, its name and how many methods it holds.
375
- function famChipLabel(family, count) {
376
- const symbol = FAM_SYMBOL[family];
377
- return '<span class="dot"></span>' + (symbol ? '<span class="sym">' + symbol + "</span> " : "") +
378
- family + ' <span class="count">&times;' + count + "</span>";
379
- }
380
-
381
- // The same hues stepped for use as text (see the --fam-*-ink tokens).
382
- const FAM_INK = {
383
- "Foundation Model": "var(--fam-foundation-ink)",
384
- "Tree-based": "var(--fam-tree-ink)",
385
- "Neural Network": "var(--fam-nn-ink)",
386
- "Reference Pipeline": "var(--fam-reference-ink)",
387
- [FAM_MERGED]: "var(--fam-baseline-ink)",
388
- };
389
-
390
- // Create an SVG element with attributes, optionally appended to `parent`.
391
- function el(name, attrs, parent) {
392
- const node = document.createElementNS(NS, name);
393
- for (const k in attrs) node.setAttribute(k, attrs[k]);
394
- if (parent) parent.appendChild(node);
395
- return node;
396
- }
397
-
398
- // Plain, ungrouped numbers with a "." decimal separator. `toFixed` is
399
- // locale-independent by definition, which is the point: `toLocaleString`
400
- // would follow the *viewer's* browser locale and print 1234,5 for a German
401
- // visitor, disagreeing with the figures and CSVs beside it.
402
- function fmtNum(v, decimals) {
403
- if (v == null || !isFinite(v)) return "—";
404
- return v.toFixed(decimals);
405
- }
406
-
407
- function fmtMetric(metric, v) {
408
- if (v == null || !isFinite(v)) return "—";
409
- return fmtNum(v, metric.decimals) + (metric.suffix || "");
410
- }
411
-
412
- function fmtTime(v) {
413
- if (v >= 100) return fmtNum(v, 0) + " s";
414
- if (v >= 1) return fmtNum(v, 1) + " s";
415
- if (v >= 0.1) return fmtNum(v, 2) + " s";
416
- return fmtNum(v, 3) + " s";
417
- }
418
-
419
- // Smallest "nice" (1/2/2.5/5 x a power of ten) step that is at least `raw`.
420
- function niceStep(raw) {
421
- if (!(raw > 0)) return 1;
422
- const mag = Math.pow(10, Math.floor(Math.log10(raw)));
423
- for (const m of [1, 2, 2.5, 5, 10]) {
424
- if (mag * m >= raw) return mag * m;
425
- }
426
- return mag * 10;
427
- }
428
-
429
- // ~`target` evenly spaced "nice" tick values covering [min, max]. Stepped by
430
- // index rather than by accumulation so fractional steps do not drift.
431
- function ticks(min, max, target) {
432
- const step = niceStep((max - min) / target);
433
- const first = Math.ceil(min / step);
434
- const out = [];
435
- for (let i = 0; first * step + i * step <= max + 1e-9; i++) out.push((first + i) * step);
436
- return out.length ? out : [min];
437
- }
438
-
439
- // A hover tooltip anchored inside `box` (which must be position:relative).
440
- function makeTooltip(box) {
441
- const node = box.querySelector(".tooltip");
442
- return {
443
- show(html, ev) { node.innerHTML = html; node.style.display = "block"; this.move(ev); },
444
- move(ev) {
445
- const r = box.getBoundingClientRect();
446
- let tx = ev.clientX - r.left + 14;
447
- const ty = ev.clientY - r.top + 12;
448
- if (tx > r.width - 270) tx = ev.clientX - r.left - 274;
449
- node.style.left = tx + "px";
450
- node.style.top = ty + "px";
451
- },
452
- hide() { node.style.display = "none"; },
453
- };
454
- }
455
-
456
- // Paper view — white surface, legend + chart only — is the *default*: what a
457
- // reader wants first is the figure, and it is the state worth exporting. The
458
- // controls, chip list and data table are one click away behind "Edit view".
459
- // `afterToggle` re-renders charts whose size is measured from the layout.
460
- // `options.openInPaper` (default true) decides the state the page opens in. A
461
- // chart opens as the figure; the leaderboard table opens with its controls,
462
- // since there the interaction is the point rather than scaffolding around it.
463
- function setUpPaperView(afterToggle, options) {
464
- const opts = options || {};
465
- const root = document.documentElement;
466
- let hostTheme = null; // the embedding page's choice, captured on entry
467
- const btn = document.getElementById("btn-paper");
468
- const embedded = window.parent !== window;
469
-
470
- function setPaper(on) {
471
- document.body.classList.toggle("paper", on);
472
- if (on) {
473
- hostTheme = root.getAttribute("data-theme");
474
- root.setAttribute("data-theme", "light");
475
- } else if (hostTheme) {
476
- root.setAttribute("data-theme", hostTheme);
477
- } else {
478
- root.removeAttribute("data-theme");
479
- }
480
- btn.textContent = on ? "Edit view" : "Paper view";
481
- document.getElementById("exportbar").hidden = !on || embedded;
482
- if (afterToggle) requestAnimationFrame(afterToggle);
483
- postHeight();
484
- }
485
- btn.addEventListener("click", () => setPaper(!document.body.classList.contains("paper")));
486
- // Embedded, the host page owns these controls — they sit beside the panel's
487
- // static-figure toggle and are driven from the outside. Standalone (the
488
- // shareable single file) this page needs its own.
489
- if (embedded) document.querySelector(".viewbar").hidden = true;
490
- window.addEventListener("message", ev => {
491
- const d = ev.data;
492
- if (d && d.type === "tabarena-explorer-paper" && typeof d.on === "boolean") setPaper(d.on);
493
- });
494
- // Only standalone: embedded, the host owns the button and would not see the
495
- // key press, so its label would fall out of step with the frame.
496
- if (!embedded) {
497
- document.addEventListener("keydown", ev => {
498
- if (ev.key === "Escape" && !document.body.classList.contains("paper")) setPaper(true);
499
- });
500
- }
501
- setPaper(opts.openInPaper !== false); // for a chart, the figure is what opens
502
- }
503
-
504
- // --- Figure export ---------------------------------------------------------
505
- // The chart is live SVG, so a file can be built from it directly. Three things
506
- // a copy has to fix up: the colors are CSS custom properties (var(--x) means
507
- // nothing outside this document), it has no background or font of its own, and
508
- // the legend is HTML rather than part of the SVG.
509
-
510
- // Rebuild the HTML legend as SVG, reusing its live layout: each item's glyph is
511
- // cloned and its label re-emitted at the measured position. foreignObject would
512
- // be far simpler, but Chrome refuses to rasterize it onto a canvas, which would
513
- // break the PNG path.
514
- // Rewrite every var(--x) in a clone's paint attributes; they resolve to nothing
515
- // once the node leaves this document.
516
- function resolveVars(root, resolve) {
517
- for (const node of [root, ...root.querySelectorAll("*")]) {
518
- for (const attr of ["fill", "stroke"]) {
519
- const value = node.getAttribute(attr);
520
- if (value && value.includes("var(")) node.setAttribute(attr, resolve(value));
521
- }
522
- }
523
- }
524
-
525
- function legendToSvg(container, resolve) {
526
- const base = container.getBoundingClientRect();
527
- const group = document.createElementNS(NS, "g");
528
- let height = 0;
529
- for (const item of container.querySelectorAll(".item")) {
530
- const box = item.getBoundingClientRect();
531
- if (!box.width) continue;
532
- height = Math.max(height, box.bottom - base.top);
533
- let textLeft = box.left - base.left;
534
- const glyph = item.querySelector("svg");
535
- if (glyph) {
536
- const gbox = glyph.getBoundingClientRect();
537
- const wrap = el("g", {
538
- transform: `translate(${gbox.left - base.left} ${gbox.top - base.top})`,
539
- }, group);
540
- const glyphClone = glyph.cloneNode(true);
541
- resolveVars(glyphClone, resolve);
542
- wrap.appendChild(glyphClone);
543
- textLeft = gbox.right - base.left + 5;
544
- }
545
- const label = item.textContent.trim();
546
- if (!label) continue;
547
- const colored = item.querySelector("[style*='color']");
548
- const text = el("text", {
549
- x: textLeft, y: box.top - base.top + box.height / 2 + 4, "font-size": 12.5,
550
- fill: resolve(getComputedStyle(colored || item).color),
551
- }, group);
552
- text.textContent = label;
553
- }
554
- return { group, height: Math.ceil(height) };
555
- }
556
-
557
- // `parts` is a list of {svg, dx}, so a chart split across panes (the sticky
558
- // y-axis beside the scrolling plot) still exports as one figure.
559
- function buildExportSvg(parts, legendEl, pad = 10) {
560
- const rootStyle = getComputedStyle(document.documentElement);
561
- const resolve = value => String(value).replace(
562
- /var\((--[\w-]+)\)/g, (_, name) => rootStyle.getPropertyValue(name).trim() || "none");
563
- const paper = rootStyle.getPropertyValue("--paper").trim() || "#ffffff";
564
-
565
- let chartW = 0, chartH = 0;
566
- for (const part of parts) {
567
- chartW = Math.max(chartW, part.dx + Number(part.svg.getAttribute("width")));
568
- chartH = Math.max(chartH, Number(part.svg.getAttribute("height")));
569
- }
570
-
571
- const out = document.createElementNS(NS, "svg");
572
- out.setAttribute("xmlns", NS);
573
- out.setAttribute("font-family", 'system-ui, -apple-system, "Segoe UI", sans-serif');
574
- let top = pad;
575
- const later = []; // built after the width is known
576
- const legend = legendEl ? legendToSvg(legendEl, resolve) : null;
577
- if (legend && legend.height) {
578
- legend.group.setAttribute("transform", `translate(${pad} ${top})`);
579
- later.push(() => out.appendChild(legend.group));
580
- top += legend.height + 8;
581
- }
582
-
583
- const width = Math.max(chartW, legendEl ? legendEl.getBoundingClientRect().width : 0) + pad * 2;
584
- const height = top + chartH + pad;
585
- out.setAttribute("width", Math.ceil(width));
586
- out.setAttribute("height", Math.ceil(height));
587
- el("rect", { x: 0, y: 0, width: Math.ceil(width), height: Math.ceil(height), fill: paper }, out);
588
- for (const build of later) build();
589
-
590
- for (const part of parts) {
591
- const group = el("g", { transform: `translate(${part.dx + pad} ${top})` }, out);
592
- const clone = part.svg.cloneNode(true);
593
- resolveVars(clone, resolve);
594
- while (clone.firstChild) group.appendChild(clone.firstChild);
595
- }
596
- return out;
597
- }
598
-
599
- // Page title -> a safe file stem, e.g. "tabarena-leaderboard-explorer-all-tasks".
600
- function slugify(text) {
601
- return (text || "chart").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
602
- }
603
-
604
- function downloadUrl(url, filename) {
605
- const a = document.createElement("a");
606
- a.href = url;
607
- a.download = filename;
608
- document.body.appendChild(a);
609
- a.click();
610
- a.remove();
611
- }
612
-
613
- // Rasterize the export SVG into a canvas at `scale`, then hand it to `done`.
614
- function rasterize(svg, scale, done, fail) {
615
- const width = Number(svg.getAttribute("width")), height = Number(svg.getAttribute("height"));
616
- const img = new Image();
617
- img.onload = () => {
618
- const canvas = document.createElement("canvas");
619
- canvas.width = Math.round(width * scale);
620
- canvas.height = Math.round(height * scale);
621
- const ctx = canvas.getContext("2d");
622
- ctx.setTransform(scale, 0, 0, scale, 0, 0);
623
- ctx.drawImage(img, 0, 0);
624
- done(canvas, width, height);
625
- };
626
- img.onerror = fail;
627
- img.src = "data:image/svg+xml;charset=utf-8,"
628
- + encodeURIComponent(new XMLSerializer().serializeToString(svg));
629
- }
630
-
631
- // A one-page PDF wrapping the rendered figure, written by hand: a library would
632
- // cost this page its zero-dependency, single-file property. The image is stored
633
- // losslessly (raw RGB + /FlateDecode via CompressionStream) and the page is sized
634
- // in points to the figure's CSS size, so it prints at the size it appears here
635
- // and the pixels land at 96*scale dpi.
636
- async function buildPdf(canvas, cssWidth, cssHeight) {
637
- const pixels = canvas.getContext("2d").getImageData(0, 0, canvas.width, canvas.height).data;
638
- const rgb = new Uint8Array((pixels.length / 4) * 3);
639
- for (let i = 0, j = 0; i < pixels.length; i += 4, j += 3) {
640
- rgb[j] = pixels[i];
641
- rgb[j + 1] = pixels[i + 1];
642
- rgb[j + 2] = pixels[i + 2];
643
- }
644
- const deflated = new Uint8Array(await new Response(
645
- new Blob([rgb]).stream().pipeThrough(new CompressionStream("deflate"))).arrayBuffer());
646
-
647
- const encoder = new TextEncoder();
648
- const chunks = [];
649
- const offsets = [];
650
- let cursor = 0;
651
- const put = data => {
652
- const bytes = typeof data === "string" ? encoder.encode(data) : data;
653
- chunks.push(bytes);
654
- cursor += bytes.length;
655
- };
656
- const object = (id, body, stream) => {
657
- offsets[id] = cursor;
658
- put(`${id} 0 obj\n${body}\n`);
659
- if (stream) {
660
- put("stream\n");
661
- put(stream);
662
- put("\nendstream\n");
663
- }
664
- put("endobj\n");
665
- };
666
-
667
- const ptW = (cssWidth * 0.75).toFixed(2), ptH = (cssHeight * 0.75).toFixed(2);
668
- const content = `q ${ptW} 0 0 ${ptH} 0 0 cm /Im0 Do Q`;
669
- put("%PDF-1.4\n");
670
- put(new Uint8Array([0x25, 0xe2, 0xe3, 0xcf, 0xd3, 0x0a])); // binary marker
671
- object(1, "<< /Type /Catalog /Pages 2 0 R >>");
672
- object(2, "<< /Type /Pages /Kids [3 0 R] /Count 1 >>");
673
- object(3, `<< /Type /Page /Parent 2 0 R /MediaBox [0 0 ${ptW} ${ptH}] `
674
- + "/Resources << /XObject << /Im0 4 0 R >> >> /Contents 5 0 R >>");
675
- object(4, "<< /Type /XObject /Subtype /Image "
676
- + `/Width ${canvas.width} /Height ${canvas.height} /ColorSpace /DeviceRGB `
677
- + `/BitsPerComponent 8 /Filter /FlateDecode /Length ${deflated.length} >>`, deflated);
678
- object(5, `<< /Length ${content.length} >>`, content);
679
-
680
- const xref = cursor;
681
- let table = "xref\n0 6\n0000000000 65535 f \n";
682
- for (let id = 1; id <= 5; id++) table += String(offsets[id]).padStart(10, "0") + " 00000 n \n";
683
- put(table);
684
- put(`trailer\n<< /Size 6 /Root 1 0 R >>\nstartxref\n${xref}\n%%EOF\n`);
685
- return new Blob(chunks, { type: "application/pdf" });
686
- }
687
-
688
- // Wire up the export buttons; `getParts` is called per click so the file always
689
- // matches what is on screen. Returns a `run(format)` the host page can drive.
690
- function setUpExport(getParts, basename) {
691
- const buttons = {
692
- svg: document.getElementById("btn-svg"),
693
- png: document.getElementById("btn-png"),
694
- pdf: document.getElementById("btn-pdf"),
695
- };
696
- const figure = () => buildExportSvg(getParts(), document.getElementById("legendstrip"));
697
-
698
- // A sandboxed frame has no modals, so a failure is reported on the button.
699
- function complain(format) {
700
- const button = buttons[format];
701
- if (!button) return;
702
- const label = button.textContent;
703
- button.textContent = "failed";
704
- setTimeout(() => { button.textContent = label; }, 2500);
705
- }
706
-
707
- function run(format) {
708
- const svg = figure();
709
- const name = basename();
710
- if (format === "svg") {
711
- downloadUrl("data:image/svg+xml;charset=utf-8,"
712
- + encodeURIComponent(new XMLSerializer().serializeToString(svg)), name + ".svg");
713
- return;
714
- }
715
- // 3x for a screen-resolution PNG; 2x for the PDF, whose page is sized in
716
- // points so the pixels already land near 200 dpi at print size.
717
- rasterize(svg, format === "pdf" ? 2 : 3, (canvas, cssWidth, cssHeight) => {
718
- if (format === "png") {
719
- canvas.toBlob(blob => downloadUrl(URL.createObjectURL(blob), name + ".png"), "image/png");
720
- } else {
721
- buildPdf(canvas, cssWidth, cssHeight)
722
- .then(blob => downloadUrl(URL.createObjectURL(blob), name + ".pdf"))
723
- .catch(() => complain("pdf"));
724
- }
725
- }, () => complain(format));
726
- }
727
-
728
- for (const format of Object.keys(buttons)) {
729
- if (buttons[format]) buttons[format].addEventListener("click", () => run(format));
730
- }
731
- // Embedded, the buttons live in the host's panel header (see main.taExport).
732
- window.addEventListener("message", ev => {
733
- const d = ev.data;
734
- if (d && d.type === "tabarena-explorer-export" && buttons[d.format] !== undefined) run(d.format);
735
- });
736
- }
737
-
738
- // When embedded, report the content height so the host page can size the
739
- // iframe to fit (avoids an inner scrollbar). Works from a sandboxed frame.
740
- // Measure the body (viewport-independent) — documentElement.scrollHeight is
741
- // clamped to at least the iframe's current viewport, which turns the
742
- // resize round-trip into a grow-forever feedback loop. The change guard
743
- // stops re-posting once the height settles.
744
- let lastPostedHeight = 0;
745
- function postHeight() {
746
- if (window.parent === window) return;
747
- const height = Math.ceil(document.body.offsetHeight);
748
- if (Math.abs(height - lastPostedHeight) < 3) return;
749
- lastPostedHeight = height;
750
- window.parent.postMessage({ type: "tabarena-explorer-height", height: height }, "*");
751
- }
752
-
753
-
754
- const TRAJECTORY = CONFIG.mode === "trajectory";
755
-
756
- const titleEl = document.getElementById("title");
757
- if (CONFIG.title) titleEl.textContent = CONFIG.title; else titleEl.hidden = true;
758
-
759
- // Column order: chips/controls left of the chart, or mirrored.
760
- document.getElementById("grid").classList.add(CONFIG.chipsSide === "right" ? "chips-right" : "chips-left");
761
-
762
- const svg = document.getElementById("chart");
763
- const box = document.getElementById("chartbox");
764
- const chipsBox = document.getElementById("chips");
765
- const controlsBox = document.querySelector(".controls");
766
- const tip = makeTooltip(box);
767
-
768
- // Marker glyph per variant at (cx, cy); trajectories use circles everywhere.
769
- function drawMark(parent, cx, cy, variant, color, size, opacity, dataM, whiteStroke) {
770
- const common = { opacity: opacity, "data-m": dataM };
771
- let node;
772
- if (TRAJECTORY || variant === "Default" || !variant) {
773
- node = el("circle", { ...common, cx, cy, r: size, fill: color }, parent);
774
- } else if (variant === "Tuned") {
775
- const s = size * 1.75;
776
- node = el("rect", { ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1.5, fill: color }, parent);
777
- } else if (variant === "Tuned + Ens.") {
778
- const d = size * 0.95;
779
- node = el("path", {
780
- ...common,
781
- d: `M${cx - d},${cy - d} L${cx + d},${cy + d} M${cx - d},${cy + d} L${cx + d},${cy - d}`,
782
- stroke: color, "stroke-width": size * 0.62, fill: "none", "stroke-linecap": "round",
783
- }, parent);
784
- } else {
785
- // Any other variant (e.g. "Baseline", holdout types): diamond.
786
- const s = size * 1.45;
787
- node = el("rect", {
788
- ...common, x: cx - s / 2, y: cy - s / 2, width: s, height: s, rx: 1,
789
- fill: color, transform: `rotate(45 ${cx} ${cy})`,
790
- }, parent);
791
- }
792
- if (whiteStroke && variant !== "Tuned + Ens.") {
793
- node.setAttribute("stroke", "var(--card)");
794
- node.setAttribute("stroke-width", "1");
795
- }
796
- return node;
797
- }
798
-
799
- function drawImputedRing(parent, cx, cy, size, color, opacity, dataM) {
800
- el("circle", {
801
- cx, cy, r: size + 4.5, fill: "none", stroke: color, "stroke-width": 1.4,
802
- "stroke-dasharray": "3 2.5", opacity: opacity, "data-m": dataM,
803
- }, parent);
804
- }
805
-
806
- // Fat green arrow pointing into the optimal corner (mirrors the static
807
- // figures' "Optimal" arrow so both read the same way).
808
- function drawOptimalArrow(parent, lowerBetter, M, W, H) {
809
- const cornerY = lowerBetter ? H - M.b - 12 : M.t + 12;
810
- const tailY = lowerBetter ? H - M.b - 64 : M.t + 64;
811
- const cx = M.l + 12, tx = M.l + 64;
812
- const dx = cx - tx, dy = cornerY - tailY;
813
- const len = Math.hypot(dx, dy);
814
- const ux = dx / len, uy = dy / len;
815
- const headLen = 16;
816
- const bx = cx - ux * headLen, by = cornerY - uy * headLen; // head base center
817
- // line stops at the head base
818
- el("line", {
819
- x1: tx, y1: tailY, x2: bx, y2: by,
820
- stroke: "var(--optimal)", "stroke-width": 13, "stroke-linecap": "round", opacity: 0.92,
821
- }, parent);
822
- const px = -uy, py = ux; // perpendicular
823
- el("polygon", {
824
- points: `${cx},${cornerY} ${bx + px * 11},${by + py * 11} ${bx - px * 11},${by - py * 11}`,
825
- fill: "var(--optimal)", opacity: 0.92,
826
- }, parent);
827
- let angle = Math.atan2(dy, dx) * 180 / Math.PI;
828
- if (angle > 90 || angle < -90) angle += 180;
829
- const mx = (tx + bx) / 2, my = (tailY + by) / 2;
830
- const t = el("text", {
831
- x: mx, y: my, "text-anchor": "middle", "dominant-baseline": "middle",
832
- "font-size": 10.5, "font-weight": 700, fill: "#ffffff",
833
- transform: `rotate(${angle} ${mx} ${my})`,
834
- }, parent);
835
- t.textContent = "Optimal";
836
- }
837
-
838
- // ---------- data ----------
839
- const byMethod = new Map();
840
- for (const p of POINTS) {
841
- if (!byMethod.has(p.method)) byMethod.set(p.method, []);
842
- byMethod.get(p.method).push(p); // insertion order = builder's point order
843
- }
844
-
845
- const METRICS = CONFIG.metrics;
846
- let metricKey = METRICS[0].key;
847
- const metricByKey = {};
848
- for (const m of METRICS) metricByKey[m.key] = m;
849
-
850
- // Single time axis per explorer (the scatter ships inference time, the
851
- // trajectories train time).
852
- const X_AXIS = CONFIG.xAxes[0];
853
- const xKey = X_AXIS.key;
854
-
855
- function mval(p, metric) { return p[metric.key]; }
856
-
857
- function computeFront(metric) {
858
- const xk = xKey;
859
- const pts = [...POINTS].sort((a, b) =>
860
- a[xk] - b[xk] || (metric.lowerBetter ? mval(a, metric) - mval(b, metric) : mval(b, metric) - mval(a, metric)));
861
- const verts = [];
862
- const methods = new Set();
863
- let best = null;
864
- for (const p of pts) {
865
- const v = mval(p, metric);
866
- if (best === null || (metric.lowerBetter ? v < best : v > best)) {
867
- if (best !== null) verts.push([p[xk], best]);
868
- verts.push([p[xk], v]);
869
- best = v;
870
- methods.add(p.method);
871
- }
872
- }
873
- return { verts, methods };
874
- }
875
-
876
- const state = { active: new Set(computeFront(metricByKey[metricKey]).methods) };
877
-
878
- // ---------- chart ----------
879
- // A flat, fixed-height plot: two of these panels then fit on one screen.
880
- const CHART_H = 400;
881
- const M = { l: 62, r: 18, t: 14, b: 52 };
882
-
883
- function render() {
884
- const metric = metricByKey[metricKey];
885
- svg.textContent = "";
886
- const W = Math.max(360, Math.round(box.clientWidth));
887
- const H = CHART_H;
888
- svg.setAttribute("width", W);
889
- svg.setAttribute("height", H);
890
- // Keep the chip list from outgrowing the chart beside it.
891
- chipsBox.style.maxHeight = Math.max(170, H - controlsBox.offsetHeight + 20) + "px";
892
-
893
- // x scale (log)
894
- const xsAll = POINTS.map(p => p[xKey]);
895
- const xmin = Math.min(...xsAll) * 0.65, xmax = Math.max(...xsAll) * 1.6;
896
- const lx0 = Math.log10(xmin), lx1 = Math.log10(xmax);
897
- const X = v => M.l + (Math.log10(v) - lx0) / (lx1 - lx0) * (W - M.l - M.r);
898
-
899
- const vals = POINTS.map(p => mval(p, metric));
900
- let y0, y1;
901
- if (metric.fromZero) {
902
- y0 = 0; y1 = Math.max(...vals) * 1.07;
903
- } else {
904
- const pad = (Math.max(...vals) - Math.min(...vals)) * 0.07;
905
- y0 = Math.min(...vals) - pad; y1 = Math.max(...vals) + pad;
906
- }
907
- const Y = v => M.t + (1 - (v - y0) / (y1 - y0)) * (H - M.t - M.b);
908
-
909
- // grid + axes
910
- const grid = el("g", {}, svg);
911
- for (let e = Math.ceil(lx0); Math.pow(10, e) < xmax; e++) {
912
- const gx = X(Math.pow(10, e));
913
- el("line", { x1: gx, y1: M.t, x2: gx, y2: H - M.b, stroke: "var(--line)", "stroke-width": 1 }, grid);
914
- const lbl = fmtNum(Math.pow(10, e), e >= 0 ? 0 : -e);
915
- el("text", { x: gx, y: H - M.b + 20, "text-anchor": "middle", "font-size": 12.5, fill: "var(--muted)" }, grid)
916
- .textContent = lbl;
917
- }
918
- for (const yv of ticks(y0, y1, 6)) {
919
- const gy = Y(yv);
920
- el("line", { x1: M.l, y1: gy, x2: W - M.r, y2: gy, stroke: "var(--line)", "stroke-width": 1 }, grid);
921
- el("text", { x: M.l - 8, y: gy + 4, "text-anchor": "end", "font-size": 12.5, fill: "var(--muted)" }, grid)
922
- .textContent = fmtNum(yv, Number.isInteger(yv) ? 0 : metric.decimals);
923
- }
924
- el("rect", { x: M.l, y: M.t, width: W - M.l - M.r, height: H - M.t - M.b, fill: "none", stroke: "var(--line)" }, grid);
925
- el("text", {
926
- x: (M.l + W - M.r) / 2, y: H - 10, "text-anchor": "middle", "font-size": 14,
927
- "font-weight": 650, fill: "var(--ink)",
928
- }, grid).textContent = X_AXIS.axisLabel;
929
- el("text", {
930
- x: 0, y: 0, "text-anchor": "middle", "font-size": 14, "font-weight": 650, fill: "var(--ink)",
931
- transform: `translate(16 ${(M.t + H - M.b) / 2}) rotate(-90)`,
932
- }, grid).textContent = metric.axisLabel;
933
-
934
- drawOptimalArrow(grid, metric.lowerBetter, M, W, H);
935
-
936
- // pareto front (always shown)
937
- const front = computeFront(metric);
938
- const fv = front.verts;
939
- if (fv.length) {
940
- let d = `M${X(fv[0][0])},${metric.lowerBetter ? M.t : H - M.b}`;
941
- for (const [fx, fy] of fv) d += ` L${X(fx)},${Y(fy)}`;
942
- d += ` L${W - M.r},${Y(fv[fv.length - 1][1])}`;
943
- el("path", { d, fill: "none", stroke: "var(--ink)", "stroke-width": 1.6, "stroke-dasharray": "7 5", opacity: 0.85 }, svg);
944
- }
945
-
946
- const isOn = m => state.active.has(m);
947
-
948
- // connectors: variant links (scatter) / the trajectory itself
949
- const conn = el("g", {}, svg);
950
- for (const [method, pts] of byMethod) {
951
- if (pts.length < 2) continue;
952
- const on = isOn(method);
953
- if (!TRAJECTORY && !on) continue; // scatter: connectors only for active methods
954
- const dd = pts.map((p, i) => `${i ? "L" : "M"}${X(p[xKey])},${Y(mval(p, metric))}`).join(" ");
955
- el("path", {
956
- d: dd, fill: "none",
957
- stroke: on ? FAM_VAR[pts[0].family] : "var(--pt-muted)",
958
- "stroke-width": on ? (TRAJECTORY ? 2 : 1.4) : 1,
959
- opacity: on ? 0.6 : 0.35,
960
- "data-m": method,
961
- }, conn);
962
- }
963
-
964
- // points: inactive first, active on top
965
- const ptsOff = el("g", {}, svg);
966
- const ptsOn = el("g", {}, svg);
967
- for (const [method, pts] of byMethod) {
968
- const on = isOn(method);
969
- for (const p of pts) {
970
- const color = on ? FAM_VAR[p.family] : "var(--pt-muted)";
971
- const size = (on ? 7 : 5) * (TRAJECTORY ? 0.8 : 1);
972
- const op = on ? 0.95 : 0.5;
973
- drawMark(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), p.variant, color, size, op, p.method, on);
974
- // Imputation ring: every affected point in scatter mode; only the
975
- // trajectory's end point in trajectory mode (a ring on all ~8 line
976
- // points would read as beads, and the chip's ‡ already flags the line).
977
- if (p.imputed && (!TRAJECTORY || p === pts[pts.length - 1])) {
978
- drawImputedRing(on ? ptsOn : ptsOff, X(p[xKey]), Y(mval(p, metric)), size, color, op, p.method);
979
- }
980
- }
981
- }
982
-
983
- // labels for active methods at their best point, greedy de-overlap
984
- const labels = [];
985
- for (const [method, pts] of byMethod) {
986
- if (!isOn(method)) continue;
987
- const best = pts.reduce((a, b) =>
988
- (metric.lowerBetter ? mval(a, metric) < mval(b, metric) : mval(a, metric) > mval(b, metric)) ? a : b);
989
- labels.push({ method, family: best.family, x: X(best[xKey]) + 10, y: Y(mval(best, metric)) - 10 });
990
- }
991
- labels.sort((a, b) => a.y - b.y);
992
- for (let i = 1; i < labels.length; i++) {
993
- for (let j = 0; j < i; j++) {
994
- if (Math.abs(labels[i].x - labels[j].x) < 110 && Math.abs(labels[i].y - labels[j].y) < 15) {
995
- labels[i].y = labels[j].y + 15;
996
- }
997
- }
998
- }
999
- const lg = el("g", {}, svg);
1000
- for (const l of labels) {
1001
- const t = el("text", {
1002
- x: Math.min(l.x, W - M.r - 8), y: Math.max(l.y, M.t + 12), "font-size": 13, "font-weight": 700,
1003
- fill: FAM_VAR[l.family], "paint-order": "stroke", stroke: "var(--card)", "stroke-width": 3.5,
1004
- "text-anchor": l.x > W - 120 ? "end" : "start",
1005
- }, lg);
1006
- t.textContent = l.method;
1007
- }
1008
-
1009
- // invisible hit targets on top (bigger than marks)
1010
- const hits = el("g", {}, svg);
1011
- for (const p of POINTS) {
1012
- const h = el("circle", { cx: X(p[xKey]), cy: Y(mval(p, metric)), r: 12, fill: "transparent", cursor: "pointer" }, hits);
1013
- h.addEventListener("mouseenter", ev => showTip(p, ev));
1014
- h.addEventListener("mousemove", ev => tip.move(ev));
1015
- h.addEventListener("mouseleave", () => hideTip(p.method));
1016
- h.addEventListener("click", () => toggle(p.method));
1017
- }
1018
- }
1019
-
1020
- // Temporary hover emphasis without a re-render (a re-render would replace
1021
- // the hit node under the cursor mid-hover).
1022
- function emphasize(method, on) {
1023
- svg.querySelectorAll(`[data-m="${CSS.escape(method)}"]`).forEach(n => {
1024
- if (on) {
1025
- if (!n.dataset.save) n.dataset.save = n.getAttribute("opacity") || "1";
1026
- n.setAttribute("opacity", "0.95");
1027
- } else if (n.dataset.save) {
1028
- n.setAttribute("opacity", n.dataset.save);
1029
- delete n.dataset.save;
1030
- }
1031
- });
1032
- }
1033
-
1034
- function showTip(p, ev) {
1035
- emphasize(p.method, true);
1036
- const sub = TRAJECTORY ? (p.n_configs != null ? `${p.n_configs} configs` : "") : (p.variant || "");
1037
- let html = `<div class="t-name">${p.method}` + (sub ? ` <span class="t-var">(${sub})</span>` : "") + "</div>" +
1038
- `<div>${p.family}</div>`;
1039
- for (const m of METRICS) {
1040
- html += `<div>${m.label}: <b>${fmtMetric(m, mval(p, m))}</b></div>`;
1041
- }
1042
- html += `<div>${X_AXIS.short}: <b>${fmtTime(p[xKey])}</b></div>`;
1043
- if (p.imputed) html += `<div class="t-imp">Imputed on ${fmtNum(p.imputed_pct, 0)}% of datasets</div>`;
1044
- tip.show(html, ev);
1045
- }
1046
- function hideTip(method) {
1047
- emphasize(method, false);
1048
- tip.hide();
1049
- }
1050
-
1051
- // ---------- chips ----------
1052
- const chipByMethod = new Map();
1053
- const famChips = new Map();
1054
- function familyMethods(fam) {
1055
- return [...byMethod.keys()].filter(m => byMethod.get(m)[0].family === fam);
1056
- }
1057
- // Chips are listed by leaderboard rank — best Elo first when Elo is
1058
- // configured, otherwise best value of the primary metric.
1059
- const RANK_METRIC = metricByKey["elo"] || metricByKey[METRICS[0].key];
1060
- function bestVal(method) {
1061
- const vals = byMethod.get(method).map(p => mval(p, RANK_METRIC));
1062
- return RANK_METRIC.lowerBetter ? Math.min(...vals) : Math.max(...vals);
1063
- }
1064
- function rankSorted(methods) {
1065
- return [...methods].sort((a, b) =>
1066
- RANK_METRIC.lowerBetter ? bestVal(a) - bestVal(b) : bestVal(b) - bestVal(a));
1067
- }
1068
- function buildChips() {
1069
- for (const fam of FAM_ORDER) {
1070
- const methods = rankSorted(familyMethods(fam));
1071
- if (!methods.length) continue;
1072
- const row = document.createElement("div");
1073
- row.className = "chiprow";
1074
- const famBtn = document.createElement("button");
1075
- famBtn.className = "famchip";
1076
- famBtn.style.setProperty("--fam", FAM_VAR[fam]);
1077
- famBtn.innerHTML = famChipLabel(fam, methods.length);
1078
- famBtn.title = `Toggle all ${methods.length} ${fam} methods`;
1079
- famBtn.addEventListener("click", () => toggleFamily(fam));
1080
- row.appendChild(famBtn);
1081
- famChips.set(fam, famBtn);
1082
- const set = document.createElement("div");
1083
- set.className = "chipset";
1084
- for (const m of methods) {
1085
- const b = document.createElement("button");
1086
- b.className = "chip";
1087
- b.style.setProperty("--fam", FAM_VAR[fam]);
1088
- const imputed = byMethod.get(m).some(p => p.imputed);
1089
- const label = document.createElement("span");
1090
- label.textContent = m;
1091
- b.appendChild(Object.assign(document.createElement("span"), { className: "dot" }));
1092
- b.appendChild(label);
1093
- if (imputed) {
1094
- const mark = document.createElement("span");
1095
- mark.className = "imp-mark";
1096
- mark.textContent = "‡";
1097
- b.appendChild(mark);
1098
- }
1099
- b.title = m + (imputed ? " — partially imputed" : "");
1100
- b.addEventListener("click", () => toggle(m));
1101
- set.appendChild(b);
1102
- chipByMethod.set(m, b);
1103
- }
1104
- row.appendChild(set);
1105
- chipsBox.appendChild(row);
1106
- }
1107
- }
1108
- function syncChips() {
1109
- for (const [m, b] of chipByMethod) b.setAttribute("aria-pressed", String(state.active.has(m)));
1110
- for (const [fam, b] of famChips) {
1111
- b.setAttribute("aria-pressed", String(familyMethods(fam).every(m => state.active.has(m))));
1112
- }
1113
- }
1114
- function toggle(m) {
1115
- if (state.active.has(m)) state.active.delete(m); else state.active.add(m);
1116
- syncChips();
1117
- render();
1118
- }
1119
- function toggleFamily(fam) {
1120
- const methods = familyMethods(fam);
1121
- const allOn = methods.every(m => state.active.has(m));
1122
- for (const m of methods) {
1123
- if (allOn) state.active.delete(m); else state.active.add(m);
1124
- }
1125
- syncChips();
1126
- render();
1127
- }
1128
- function setActive(methods) {
1129
- state.active = new Set(methods);
1130
- syncChips();
1131
- render();
1132
- }
1133
- document.getElementById("btn-front").addEventListener("click",
1134
- () => setActive(computeFront(metricByKey[metricKey]).methods));
1135
- document.getElementById("btn-all").addEventListener("click", () => setActive([...byMethod.keys()]));
1136
- document.getElementById("btn-none").addEventListener("click", () => setActive([]));
1137
-
1138
- // metric selector (hidden when only one metric is configured)
1139
- const metricPick = document.getElementById("metricpick");
1140
- const metricSelect = document.getElementById("metric-select");
1141
- if (METRICS.length > 1) {
1142
- metricPick.hidden = false;
1143
- for (const m of METRICS) {
1144
- const opt = document.createElement("option");
1145
- opt.value = m.key;
1146
- opt.textContent = m.label;
1147
- metricSelect.appendChild(opt);
1148
- }
1149
- metricSelect.addEventListener("change", ev => {
1150
- metricKey = ev.target.value;
1151
- render();
1152
- });
1153
- }
1154
-
1155
- // ---------- legend strip ----------
1156
- function buildLegend() {
1157
- const box2 = document.getElementById("legendstrip");
1158
- let html = "";
1159
- if (!TRAJECTORY) {
1160
- html +=
1161
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><circle cx="7" cy="7" r="5" fill="var(--muted)"/></svg> Default</span>' +
1162
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><rect x="2" y="2" width="10" height="10" rx="1.5" fill="var(--muted)"/></svg> Tuned</span>' +
1163
- '<span class="item"><svg width="14" height="14" viewBox="0 0 14 14"><path d="M3,3 L11,11 M3,11 L11,3" stroke="var(--muted)" stroke-width="2.6" stroke-linecap="round"/></svg> Tuned + Ensembled</span>';
1164
- } else {
1165
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--muted)" stroke-width="2"/><circle cx="6" cy="4" r="2.6" fill="var(--muted)"/><circle cx="16" cy="4" r="2.6" fill="var(--muted)"/></svg> Tuning trajectory (more configs &rarr; more time)</span>';
1166
- }
1167
- html += '<span class="item"><svg width="26" height="8" viewBox="0 0 26 8"><line x1="0" y1="4" x2="26" y2="4" stroke="var(--ink)" stroke-width="1.6" stroke-dasharray="6 4"/></svg> Pareto front (always shown)</span>';
1168
- if (POINTS.some(p => p.imputed)) {
1169
- html += '<span class="item"><svg width="18" height="18" viewBox="0 0 18 18"><circle cx="9" cy="9" r="4" fill="var(--muted)"/><circle cx="9" cy="9" r="7.5" fill="none" stroke="var(--muted)" stroke-width="1.3" stroke-dasharray="3 2.5"/></svg> &Dagger; partially imputed</span>';
1170
- }
1171
- // Model family, named: highlighted points are colored by family, and in paper
1172
- // view the chip list that would otherwise decode them is hidden.
1173
- const families = FAM_ORDER.filter(f => POINTS.some(p => p.family === f));
1174
- if (families.length > 1) {
1175
- html += '<span class="legendbreak"></span><span class="item">Family:</span>';
1176
- for (const fam of families) {
1177
- html += `<span class="item"><svg width="12" height="12" viewBox="0 0 12 12">` +
1178
- `<circle cx="6" cy="6" r="5" fill="${FAM_VAR[fam]}"/></svg> ` +
1179
- `<span style="color:${FAM_INK[fam]}">${fam}</span></span>`;
1180
- }
1181
- }
1182
- box2.innerHTML = html;
1183
- }
1184
-
1185
- // ---------- paper view ----------
1186
- setUpPaperView(render);
1187
- setUpExport(() => [{ svg: svg, dx: 0 }], () => slugify(document.title));
1188
-
1189
- // ---------- data table ----------
1190
- function buildTable() {
1191
- const m0 = metricByKey[METRICS[0].key];
1192
- const rows = [...POINTS].sort((a, b) =>
1193
- m0.lowerBetter ? mval(a, m0) - mval(b, m0) : mval(b, m0) - mval(a, m0));
1194
- let html = "<table><thead><tr><th>Method</th>";
1195
- html += TRAJECTORY ? "<th>Configs</th>" : "<th>Variant</th>";
1196
- html += "<th>Family</th>";
1197
- for (const m of METRICS) html += `<th>${m.label}</th>`;
1198
- html += `<th>${X_AXIS.short}</th><th>Imputed</th></tr></thead><tbody>`;
1199
- for (const p of rows) {
1200
- html += `<tr><td>${p.method}</td><td>${TRAJECTORY ? (p.n_configs != null ? p.n_configs : "—") : p.variant}</td><td>${p.family}</td>`;
1201
- for (const m of METRICS) html += `<td>${fmtMetric(m, mval(p, m))}</td>`;
1202
- html += `<td>${fmtNum(p[xKey], 3)}</td>`;
1203
- html += `<td>${p.imputed ? fmtNum(p.imputed_pct, 0) + "%" : "—"}</td></tr>`;
1204
- }
1205
- html += "</tbody></table>";
1206
- document.getElementById("tblwrap").innerHTML = html;
1207
- }
1208
-
1209
- const _renderInner = render;
1210
- render = function () {
1211
- _renderInner();
1212
- postHeight();
1213
- };
1214
- document.querySelector("details.datatable").addEventListener("toggle", postHeight);
1215
- let resizeTimer = null;
1216
- window.addEventListener("resize", () => {
1217
- clearTimeout(resizeTimer);
1218
- resizeTimer = setTimeout(render, 120);
1219
- });
1220
- window.addEventListener("load", postHeight);
1221
-
1222
- buildChips();
1223
- buildLegend();
1224
- buildTable();
1225
- syncChips();
1226
- render();
1227
- })();
1228
- </script>
1229
- </body>
1230
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_improvability_vs_time_infer.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:cf51441148edad29a2482f34433c299274409cb28be2a14cf7f3dc0c47832872
3
- size 275180
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/pareto_front_points.csv DELETED
@@ -1,63 +0,0 @@
1
- method,variant,family,x_infer,x_train,imp,elo,imputed,imputed_pct
2
- TabFM,Default,Foundation Model,5.357838133261318,39.5720422516442,4.654355254106112,1743.7,False,0.0
3
- TabPFN-3,Default,Foundation Model,0.4158216638576948,9.169929954447653,10.76580500520397,1636.0,False,0.0
4
- TabPFN-2.6,Default,Foundation Model,0.5482886619733942,7.034289077293122,11.96074156251044,1599.2,False,0.0
5
- RealTabPFN-2.5,Tuned + Ens.,Foundation Model,8.047594848219795,2289.3887157667205,11.197577244820794,1595.2,False,0.0
6
- TabICLv2,Default,Foundation Model,0.1520731744535504,3.9320097545321064,11.560408578460901,1572.2,False,0.0
7
- RealTabPFN-2.5,Tuned,Foundation Model,1.1425914844543796,2289.3887157667205,11.907118072301099,1555.4,False,0.0
8
- RealTabPFN-2.5,Default,Foundation Model,0.6362272807949252,6.7587899978217045,12.320088737585506,1541.9,False,0.0
9
- RealMLP,Tuned + Ens.,Neural Network,21.902496279105236,3771.8540221067997,14.53336029824196,1479.7,False,0.0
10
- TabDPT-Turbo,Default,Foundation Model,0.19227067519018423,4.691113094573359,15.329550194505407,1448.5,False,0.0
11
- TabDPT,Tuned + Ens.,Foundation Model,218.45336407352897,5119.776985103065,15.246470826087291,1448.0,False,0.0
12
- TabM,Tuned + Ens.,Neural Network,3.8381409685971137,2585.579863640918,15.6750950369016,1429.1,False,0.0
13
- TabDPT,Tuned,Foundation Model,28.351340383207884,5119.776985103065,15.883723432046265,1415.3,False,0.0
14
- LimiX,Default,Foundation Model,6.828482678301318,35.50961056314309,15.154885083708347,1405.3,False,0.0
15
- RealMLP,Tuned,Neural Network,1.7776750936346917,3771.8540221067997,15.90978346527886,1399.9,False,0.0
16
- LightGBM,Tuned + Ens.,Tree-based,2.5704534160747796,892.4854074789564,16.936687658650524,1389.8,False,0.0
17
- TabM,Tuned,Neural Network,0.4361574183230644,2585.579863640918,16.56097328725307,1373.4,False,0.0
18
- CatBoost,Tuned + Ens.,Tree-based,0.39602676030046535,1739.1690497297914,17.040642856753067,1367.6,False,0.0
19
- TabSwift,Default,Foundation Model,0.05791268987655686,2.756051243933877,17.22410804787243,1363.2,False,0.0
20
- iLTM,Tuned + Ens.,Foundation Model,841.445890748085,22811.744051243128,17.42147869511167,1361.3,False,0.0
21
- CatBoost,Tuned,Tree-based,0.050853266781756806,1739.1690497297914,17.278859448847676,1354.2,False,0.0
22
- LightGBM,Tuned,Tree-based,0.3458375857654502,892.4854074789564,17.38939889928507,1353.4,False,0.0
23
- TabDPT,Default,Foundation Model,43.707401035938005,50.32502415266903,18.02306483084254,1331.6,False,0.0
24
- xRFM,Tuned + Ens.,Other,2.9395549707468733,1126.9686387245144,17.868361221740837,1330.4,False,0.0
25
- XGBoost,Tuned + Ens.,Tree-based,2.3674863948529943,884.4808277373293,17.92374283473731,1324.7,False,0.0
26
- ModernNCA,Tuned,Neural Network,0.5053503954101921,6523.550336157642,18.056130336227604,1323.4,False,0.0
27
- ModernNCA,Tuned + Ens.,Neural Network,8.390322625318314,6523.550336157642,18.318760983199105,1323.2,False,0.0
28
- CatBoost,Default,Tree-based,0.0455896073914258,8.462590335549091,18.2032871809493,1320.5,False,0.0
29
- SAP-RPT-OSS,Default,Foundation Model,1.2773508676992504,16.230459759105607,18.838837170210166,1319.4,False,0.0
30
- XGBoost,Tuned,Tree-based,0.3929683730676072,884.4808277373293,18.132841379402812,1306.4,False,0.0
31
- TabM,Default,Neural Network,0.4449377919636944,9.936009218949234,18.257834368806115,1295.8,False,0.0
32
- xRFM,Tuned,Other,0.16324468073957168,1126.9686387245144,18.89381823983451,1282.4,False,0.0
33
- ChimeraBoost,Tuned + Ens.,Tree-based,0.45490386498083757,1218.8724153795765,19.516024658787416,1278.2,False,0.0
34
- TorchMLP,Tuned + Ens.,Neural Network,2.1570271396274556,3865.9507169384297,19.12433023409986,1260.5,False,0.0
35
- ChimeraBoost,Tuned,Tree-based,0.03598330933688246,1218.8724153795765,19.80334339083033,1259.9,False,0.0
36
- EBM,Tuned + Ens.,Tree-based,0.15296939610611437,2499.3997394448907,19.95446510594069,1259.8,False,0.0
37
- iLTM,Tuned,Foundation Model,161.22064740025013,22811.744051243128,19.245205487016797,1257.7,False,0.0
38
- ModernNCA,Default,Neural Network,0.32060068900263267,16.279623910227823,21.22087021471567,1237.3,False,0.0
39
- EBM,Tuned,Tree-based,0.017293833845938654,2499.3997394448907,20.51054592802531,1230.9,False,0.0
40
- RealMLP,Default,Neural Network,4.688867646313849,15.69323516601608,20.018466958314622,1221.0,False,0.0
41
- ChimeraBoost,Default,Tree-based,0.04138894865138677,5.4899520078266,20.940984645291863,1216.2,False,0.0
42
- FastaiMLP,Tuned + Ens.,Neural Network,8.068837476686,1679.3959423676156,20.712381049385332,1214.1,False,0.0
43
- ExtraTrees,Tuned + Ens.,Tree-based,1.501127939369875,822.5846846042664,21.641215765915888,1210.9,False,0.0
44
- EBM,Default,Tree-based,0.017156058727181196,7.388420291453219,21.249349120209597,1203.0,False,0.0
45
- TorchMLP,Tuned,Neural Network,0.14743941123732562,3865.9507169384297,20.861592718401972,1195.6,False,0.0
46
- ExtraTrees,Tuned,Tree-based,0.21369237168913796,822.5846846042664,22.339109350750974,1187.0,False,0.0
47
- LightGBM,Default,Tree-based,0.1695753132718836,3.608651599211503,21.229482732106593,1166.0,False,0.0
48
- FastaiMLP,Tuned,Neural Network,0.8257869199912973,1679.3959423676156,21.847378167043086,1164.8,False,0.0
49
- XGBoost,Default,Tree-based,0.2525411892114416,3.2873922338088355,21.244225857245823,1163.3,False,0.0
50
- RandomForest,Tuned + Ens.,Tree-based,1.4602306270544885,590.0394439850668,23.21550916091867,1149.5,False,0.0
51
- TabSTAR,Tuned,Foundation Model,5.606208042584258,40848.86657483013,25.49143698118923,1131.8,False,0.0
52
- TabSTAR,Tuned + Ens.,Foundation Model,25.936207253387835,40848.86657483013,25.524817856764482,1130.2,False,0.0
53
- RandomForest,Tuned,Tree-based,0.15030078963359184,590.0394439850668,23.948032253297473,1113.3,False,0.0
54
- PerpetualBooster,Tuned + Ens.,Tree-based,0.8948102983830184,342.03900946680346,28.450886617554755,1083.5,False,0.0
55
- iLTM,Default,Foundation Model,161.81343832142903,524.4765295003572,24.91360249315293,1066.4,False,0.0
56
- TorchMLP,Default,Neural Network,0.1550906391981672,13.688894957032199,24.760902778404247,1057.8,False,0.0
57
- PerpetualBooster,Tuned,Tree-based,0.3125464363490111,342.03900946680346,29.907678995353514,1048.2,False,0.0
58
- xRFM,Default,Other,1.0976028857093967,5.446030178240368,26.63871042030792,1043.3,False,0.0
59
- TabSTAR,Default,Foundation Model,7.213726442305473,471.6614585494219,30.359879360159113,1033.2,False,0.0
60
- ExtraTrees,Default,Tree-based,0.5663988088013532,4.194175284298607,27.587751498606593,1019.7,False,0.0
61
- RandomForest,Default,Tree-based,0.08856487680890945,0.5088535014091562,28.376526703294413,1000.0,False,0.0
62
- FastaiMLP,Default,Neural Network,0.6842938244078286,5.483894573958628,26.85583180391888,999.9,False,0.0
63
- PerpetualBooster,Default,Tree-based,0.040657078690582577,39.09071168560041,32.716605091837856,946.6,False,0.0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/pareto_n_configs_imp.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:9c8e41934eb6ef16329d06ac04b8db793c65701922a4ad688d432cf4ddb3cb84
3
- size 284634
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/tuning-impact-elo.png.zip DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:c4d225aebd506c0101a8f80663ba3b30195fb7255fe829dd3f88480bd4c9791a
3
- size 105251
 
 
 
 
data/imputation_no/splits_all/tasks_all/datasets_small/tuning_trajectories.csv DELETED
@@ -1,163 +0,0 @@
1
- method,family,x_train,x_infer,imp,elo,n_configs,imputed,imputed_pct
2
- CatBoost,Tree-based,8.462590335549091,0.0455896073914258,18.22801872127607,1332.9,1.0,False,0.0
3
- CatBoost,Tree-based,20.346193555734267,0.09660970481496081,17.61917864571887,1359.4,2.0,False,0.0
4
- CatBoost,Tree-based,49.72308624090884,0.18065285816060878,17.253007428845667,1378.3,5.0,False,0.0
5
- CatBoost,Tree-based,92.48508500945157,0.23983440670665684,17.118130773175917,1383.8,10.0,False,0.0
6
- CatBoost,Tree-based,216.01313992020846,0.2815893316185425,17.013055738158688,1387.4,25.0,False,0.0
7
- CatBoost,Tree-based,428.4029173434785,0.31791456478804925,17.01617892461794,1386.5,50.0,False,0.0
8
- CatBoost,Tree-based,862.8837988810473,0.38060021065068383,17.033005196506924,1384.7,100.0,False,0.0
9
- CatBoost,Tree-based,1291.843839644664,0.38736151225316695,17.05742025825018,1383.1,150.0,False,0.0
10
- CatBoost,Tree-based,1739.1690497297914,0.39602676030046535,17.06360104170376,1382.1,201.0,False,0.0
11
- ChimeraBoost,Tree-based,5.4899520078266,0.04138894865138677,20.96498333861501,1223.2,1.0,False,0.0
12
- ChimeraBoost,Tree-based,11.334103150400939,0.08018211274281706,20.65170109104463,1240.6,2.0,False,0.0
13
- ChimeraBoost,Tree-based,28.892796772186244,0.14570389969215242,20.467788555456597,1251.3,5.0,False,0.0
14
- ChimeraBoost,Tree-based,58.87203197884595,0.2046887661505208,20.295471078639483,1259.3,10.0,False,0.0
15
- ChimeraBoost,Tree-based,150.36569983602635,0.2874258032409872,19.988689801038134,1271.3,25.0,False,0.0
16
- ChimeraBoost,Tree-based,302.47871980891523,0.3447163363004163,19.831121378401004,1278.9,50.0,False,0.0
17
- ChimeraBoost,Tree-based,606.008218265041,0.4006058604567584,19.657543111625316,1285.9,100.0,False,0.0
18
- ChimeraBoost,Tree-based,908.7557510717904,0.43279691313346724,19.584581550555765,1288.3,150.0,False,0.0
19
- ChimeraBoost,Tree-based,1218.8724153795765,0.4496610990803296,19.539677456769304,1291.1,201.0,False,0.0
20
- EBM,Tree-based,7.388420291453219,0.017156058727181196,21.274096684434205,1208.5,1.0,False,0.0
21
- EBM,Tree-based,19.13979943869537,0.033315531949338485,20.997222169689923,1219.6,2.0,False,0.0
22
- EBM,Tree-based,59.65680170213522,0.06020202098143048,20.637488816426607,1238.7,5.0,False,0.0
23
- EBM,Tree-based,123.90743183396125,0.07941086088006653,20.487930220731542,1246.8,10.0,False,0.0
24
- EBM,Tree-based,318.46001064068014,0.10750903547065471,20.29035237779537,1254.9,25.0,False,0.0
25
- EBM,Tree-based,623.1113495162684,0.12378308877985164,20.153558096828956,1262.2,50.0,False,0.0
26
- EBM,Tree-based,1256.5403166692183,0.14405226317495906,20.047775272582818,1267.3,100.0,False,0.0
27
- EBM,Tree-based,1885.0271107114127,0.14697197529268866,20.009669417130617,1270.3,150.0,False,0.0
28
- EBM,Tree-based,2499.3997394448907,0.1440681396038307,19.98337359606066,1271.9,201.0,False,0.0
29
- ExtraTrees,Tree-based,4.194175284298607,0.5663988088013532,27.60941462253397,1021.2,1.0,False,0.0
30
- ExtraTrees,Tree-based,8.720078438945333,0.7011757791959015,25.885989616663245,1062.0,2.0,False,0.0
31
- ExtraTrees,Tree-based,21.685275973670596,0.7330007012763398,23.441225219730892,1129.0,5.0,False,0.0
32
- ExtraTrees,Tree-based,43.12316999769439,0.7428670659282961,22.61451466400202,1170.4,10.0,False,0.0
33
- ExtraTrees,Tree-based,107.39538737179987,1.018855954643115,22.001487160366885,1202.3,25.0,False,0.0
34
- ExtraTrees,Tree-based,210.06024633928405,1.2214373513463619,21.841068527707396,1212.3,50.0,False,0.0
35
- ExtraTrees,Tree-based,412.0764862110399,1.4038021731459822,21.736976146908678,1218.6,100.0,False,0.0
36
- ExtraTrees,Tree-based,614.240675925616,1.462695758694767,21.676622474397757,1223.4,150.0,False,0.0
37
- ExtraTrees,Tree-based,822.5846846042664,1.501127939369875,21.666099480679918,1223.9,201.0,False,0.0
38
- FastaiMLP,Neural Network,5.483894573958628,0.6842938244078286,26.87947257075784,1003.6,1.0,False,0.0
39
- FastaiMLP,Neural Network,14.530250474180118,1.4234649143152087,25.605440571288423,1045.8,2.0,False,0.0
40
- FastaiMLP,Neural Network,42.06854030037134,2.70863815575737,23.540793521493093,1120.3,5.0,False,0.0
41
- FastaiMLP,Neural Network,79.19690331067542,3.9064906778565582,22.577900990668017,1157.8,10.0,False,0.0
42
- FastaiMLP,Neural Network,202.91650957601354,5.517691743805193,21.700240847904297,1190.3,25.0,False,0.0
43
- FastaiMLP,Neural Network,413.0147999434225,6.825523560748225,21.29778091361426,1207.5,50.0,False,0.0
44
- FastaiMLP,Neural Network,829.4495951905283,7.579914014700657,21.015252947637034,1217.1,100.0,False,0.0
45
- FastaiMLP,Neural Network,1248.122171700538,7.913305846749778,20.83696614773943,1223.8,150.0,False,0.0
46
- FastaiMLP,Neural Network,1679.3959423676156,8.077227734441916,20.733431000267178,1228.0,201.0,False,0.0
47
- LightGBM,Tree-based,3.608651599211503,0.1695753132718836,21.25370273435017,1168.5,1.0,False,0.0
48
- LightGBM,Tree-based,8.865955424310329,0.7271936335358054,19.364659272170424,1257.8,2.0,False,0.0
49
- LightGBM,Tree-based,21.921505878230747,1.165245524912983,18.189015923605755,1330.5,5.0,False,0.0
50
- LightGBM,Tree-based,43.593394992671044,1.4098790833754133,17.69549232887076,1361.8,10.0,False,0.0
51
- LightGBM,Tree-based,110.8314003139697,1.8172136653942288,17.328862875062654,1386.4,25.0,False,0.0
52
- LightGBM,Tree-based,222.35789421136602,2.0744273860536486,17.20742840636462,1395.5,50.0,False,0.0
53
- LightGBM,Tree-based,444.73169339543847,2.3211352586207132,17.060104033808077,1404.9,100.0,False,0.0
54
- LightGBM,Tree-based,664.9958936778935,2.425276738483371,16.99791358203584,1408.0,150.0,False,0.0
55
- LightGBM,Tree-based,892.4854074789564,2.5817474842043104,16.96093514844105,1409.4,201.0,False,0.0
56
- LimiX,Foundation Model,35.50961056314309,6.828482678301318,15.210524360691757,1422.0,1.0,False,0.0
57
- ModernNCA,Neural Network,16.279623910227823,0.32060068900263267,21.245019708709258,1248.3,1.0,False,0.0
58
- ModernNCA,Neural Network,54.21648382537063,0.8565664694763102,20.025741966024626,1273.8,2.0,False,0.0
59
- ModernNCA,Neural Network,158.53994120196415,1.9156693463145684,18.07905172582486,1349.3,5.0,False,0.0
60
- ModernNCA,Neural Network,318.0880622722443,2.785843758633247,17.66819528267606,1366.9,10.0,False,0.0
61
- ModernNCA,Neural Network,784.3400464923684,4.219514475452051,17.252774036865365,1384.0,25.0,False,0.0
62
- ModernNCA,Neural Network,1619.6093744476316,5.574964791511255,17.44680453759096,1373.4,50.0,False,0.0
63
- ModernNCA,Neural Network,3239.1118632438247,7.063712430292479,17.866481656519923,1357.5,100.0,False,0.0
64
- ModernNCA,Neural Network,4838.988606911565,7.8596602241219475,18.173570968320877,1345.3,150.0,False,0.0
65
- ModernNCA,Neural Network,6523.550336157642,8.384898189151137,18.347650200624336,1337.3,201.0,False,0.0
66
- PerpetualBooster,Tree-based,39.09071168560041,0.040657078690582577,33.318479094829,943.0,1.0,False,0.0
67
- PerpetualBooster,Tree-based,114.19680170724877,0.3252722557923411,31.1917379277461,1003.6,2.0,False,0.0
68
- PerpetualBooster,Tree-based,285.35259642784723,0.7833048920276815,28.661518170215118,1083.2,5.0,False,0.0
69
- PerpetualBooster,Tree-based,342.03900946680346,0.8948102983830184,28.46978900579733,1090.2,6.0,False,0.0
70
- RandomForest,Tree-based,0.5088535014091562,0.08856487680890945,28.409946673399666,999.4,1.0,False,0.0
71
- RandomForest,Tree-based,3.3556051422440136,0.274181271644493,26.104844103809437,1044.0,2.0,False,0.0
72
- RandomForest,Tree-based,12.849220132990748,0.4818339926449089,24.811989847138584,1078.7,5.0,False,0.0
73
- RandomForest,Tree-based,27.96770195892894,0.678992572925596,24.054151765770364,1108.8,10.0,False,0.0
74
- RandomForest,Tree-based,70.75466255009994,0.8688853999623392,23.5114987788157,1142.5,25.0,False,0.0
75
- RandomForest,Tree-based,143.98648318866492,1.0449947563121842,23.377294359921297,1150.8,50.0,False,0.0
76
- RandomForest,Tree-based,290.43930986552186,1.2523875096341897,23.290473049028794,1155.8,100.0,False,0.0
77
- RandomForest,Tree-based,439.37065780631167,1.373768748735566,23.253662020587587,1157.7,150.0,False,0.0
78
- RandomForest,Tree-based,590.0394439850668,1.459960166778112,23.236025263760705,1158.9,201.0,False,0.0
79
- RealMLP,Neural Network,15.69323516601608,4.688867646313849,20.044269579375932,1226.9,1.0,False,0.0
80
- RealMLP,Neural Network,33.849680863187785,6.699336819048018,17.62429168840669,1331.3,2.0,False,0.0
81
- RealMLP,Neural Network,89.022575403558,10.361104313047106,15.892895926486114,1424.2,5.0,False,0.0
82
- RealMLP,Neural Network,186.9144346810765,12.414049216861697,15.349194410543715,1457.8,10.0,False,0.0
83
- RealMLP,Neural Network,469.4672918764594,14.92712763820984,14.975087750590802,1481.7,25.0,False,0.0
84
- RealMLP,Neural Network,941.4479936377176,17.204325480320684,14.796073064558938,1494.2,50.0,False,0.0
85
- RealMLP,Neural Network,1881.3658212549644,19.31438802167681,14.69171546627355,1499.1,100.0,False,0.0
86
- RealMLP,Neural Network,2818.5782940853887,20.502682004182418,14.625770234730705,1503.0,150.0,False,0.0
87
- RealMLP,Neural Network,3771.8540221067997,21.902496279105236,14.561284232977489,1503.7,201.0,False,0.0
88
- RealTabPFN-2.5,Foundation Model,6.7587899978217045,0.6362272807949252,12.347361057130597,1560.3,1.0,False,0.0
89
- RealTabPFN-2.5,Foundation Model,20.022077391499224,1.3637258308022322,12.279214993920386,1565.3,2.0,False,0.0
90
- RealTabPFN-2.5,Foundation Model,54.35009239022621,2.776795940260542,11.894270631404149,1583.4,5.0,False,0.0
91
- RealTabPFN-2.5,Foundation Model,108.98790128787763,4.002983246839659,11.698528996854291,1602.2,10.0,False,0.0
92
- RealTabPFN-2.5,Foundation Model,284.7684460336686,5.7106402075573115,11.497664190753204,1612.7,25.0,False,0.0
93
- RealTabPFN-2.5,Foundation Model,566.4138152118644,6.793591917858365,11.389525351312809,1616.4,50.0,False,0.0
94
- RealTabPFN-2.5,Foundation Model,1137.2039338041147,7.389127054143678,11.31389385903684,1617.8,100.0,False,0.0
95
- RealTabPFN-2.5,Foundation Model,1710.4116369596145,7.871864394392892,11.27848674027812,1617.9,150.0,False,0.0
96
- RealTabPFN-2.5,Foundation Model,2289.3887157667205,8.353344818085464,11.234666170306504,1616.2,201.0,False,0.0
97
- SAP-RPT-OSS,Foundation Model,16.230459759105607,1.2773508676992504,18.86416005851036,1334.7,1.0,False,0.0
98
- TabDPT,Foundation Model,50.32502415266903,43.707401035938005,18.046661408364585,1344.8,1.0,False,0.0
99
- TabDPT,Foundation Model,73.76989416372965,52.11201629009561,17.357261978922008,1371.3,2.0,False,0.0
100
- TabDPT,Foundation Model,139.81847708335647,66.0787878176586,16.66239317547098,1406.0,5.0,False,0.0
101
- TabDPT,Foundation Model,256.4808826588974,83.6221921836606,16.33348620385194,1422.6,10.0,False,0.0
102
- TabDPT,Foundation Model,631.0078513375786,110.24799428469449,15.78109587578097,1444.2,25.0,False,0.0
103
- TabDPT,Foundation Model,1275.2299790654088,138.47537424399025,15.519607804715541,1455.2,50.0,False,0.0
104
- TabDPT,Foundation Model,2539.0709530625045,167.42760567363604,15.363851740247936,1463.0,100.0,False,0.0
105
- TabDPT,Foundation Model,3811.208024605316,188.43165590675713,15.300477535033266,1465.0,150.0,False,0.0
106
- TabDPT,Foundation Model,5119.776985103065,214.0824864784409,15.276643056444033,1465.8,201.0,False,0.0
107
- TabDPT-Turbo,Foundation Model,4.691113094573359,0.19227067519018423,15.353850262933463,1462.3,1.0,False,0.0
108
- TabFM,Foundation Model,39.5720422516442,5.357838133261318,4.6820290320989635,1759.4,1.0,False,0.0
109
- TabICLv2,Foundation Model,3.9320097545321064,0.1520731744535504,11.587733779799482,1591.2,1.0,False,0.0
110
- TabM,Neural Network,9.936009218949234,0.4449377919636944,18.283509667248886,1306.3,1.0,False,0.0
111
- TabM,Neural Network,21.74621272792782,0.8480171202367977,17.894564704745637,1326.5,2.0,False,0.0
112
- TabM,Neural Network,59.987973874757685,1.6121786403349327,17.43245203223984,1355.9,5.0,False,0.0
113
- TabM,Neural Network,127.11365194789559,2.0828680316925148,16.877134813139428,1388.9,10.0,False,0.0
114
- TabM,Neural Network,318.70836323157647,2.669847945333597,16.408830515598016,1413.4,25.0,False,0.0
115
- TabM,Neural Network,637.1067463952677,3.1238178806065933,16.117502164814745,1428.6,50.0,False,0.0
116
- TabM,Neural Network,1278.8911702563391,3.484415328611071,15.923231118886452,1439.4,100.0,False,0.0
117
- TabM,Neural Network,1925.3471149571128,3.6365060901989694,15.755545263622809,1445.3,150.0,False,0.0
118
- TabM,Neural Network,2585.579863640918,3.8381409685971137,15.701798864789993,1449.6,201.0,False,0.0
119
- TabPFN-2.6,Foundation Model,7.034289077293122,0.5482886619733942,11.987793670213447,1622.7,1.0,False,0.0
120
- TabPFN-3,Foundation Model,9.169929954447653,0.4158216638576948,10.794768217873633,1658.6,1.0,False,0.0
121
- TabSTAR,Foundation Model,471.6614585494219,7.213726442305473,30.38344157705401,1039.3,1.0,False,0.0
122
- TabSTAR,Foundation Model,797.2338293006283,8.246177063271318,30.237553936314576,1046.8,2.0,False,0.0
123
- TabSTAR,Foundation Model,1402.6890753281787,10.34737673805446,29.924190478466507,1055.6,5.0,False,0.0
124
- TabSTAR,Foundation Model,2175.087851949379,11.90188157751366,29.172366849727666,1073.6,10.0,False,0.0
125
- TabSTAR,Foundation Model,5045.522576046773,16.22592614581593,27.879960409008085,1096.7,25.0,False,0.0
126
- TabSTAR,Foundation Model,10226.355347764464,19.60206628408606,26.676519669081177,1116.4,50.0,False,0.0
127
- TabSTAR,Foundation Model,20731.065214537048,22.976985584065375,26.05309621070424,1130.9,100.0,False,0.0
128
- TabSTAR,Foundation Model,30617.587787993205,24.374053334119353,25.758833587093893,1138.9,150.0,False,0.0
129
- TabSTAR,Foundation Model,40848.86657483013,25.936207253387835,25.549963365203965,1146.9,201.0,False,0.0
130
- TabSwift,Foundation Model,2.756051243933877,0.05791268987655686,17.248500496240368,1378.7,1.0,False,0.0
131
- TorchMLP,Neural Network,13.688894957032199,0.1550906391981672,24.777879162175854,1066.7,1.0,False,0.0
132
- TorchMLP,Neural Network,32.17994192224896,0.31084197868517116,22.857678180346667,1125.6,2.0,False,0.0
133
- TorchMLP,Neural Network,92.74701914680642,0.6204009276541033,21.269571665469336,1176.9,5.0,False,0.0
134
- TorchMLP,Neural Network,198.403788246261,0.8859862453217204,20.581217131435597,1207.4,10.0,False,0.0
135
- TorchMLP,Neural Network,483.47393881962074,1.278904806328708,19.98364615139698,1234.2,25.0,False,0.0
136
- TorchMLP,Neural Network,974.006991783206,1.6522358141598805,19.609261912778692,1252.9,50.0,False,0.0
137
- TorchMLP,Neural Network,1885.3999284295999,1.896272359232266,19.36020456048911,1264.1,100.0,False,0.0
138
- TorchMLP,Neural Network,2882.886561733708,2.0769295602160094,19.23934880995436,1271.3,150.0,False,0.0
139
- TorchMLP,Neural Network,3865.9507169384297,2.1462178944525663,19.14239455337929,1276.2,201.0,False,0.0
140
- XGBoost,Tree-based,3.2873922338088355,0.2525411892114416,21.26768770126012,1165.7,1.0,False,0.0
141
- XGBoost,Tree-based,8.297510696816628,0.6687217930403135,19.35164883335737,1250.3,2.0,False,0.0
142
- XGBoost,Tree-based,21.70877790629026,1.3135984732073058,18.82692276742906,1282.1,5.0,False,0.0
143
- XGBoost,Tree-based,42.697141308868254,1.473664156253216,18.528925418023455,1303.0,10.0,False,0.0
144
- XGBoost,Tree-based,109.82739796316375,1.8610108954727544,18.285221069896405,1318.5,25.0,False,0.0
145
- XGBoost,Tree-based,219.73481796370584,2.0831926002011665,18.182502263747296,1324.9,50.0,False,0.0
146
- XGBoost,Tree-based,439.60203143007277,2.243969897476304,18.0937437910342,1331.2,100.0,False,0.0
147
- XGBoost,Tree-based,659.3117486381539,2.3416077193265945,18.01469070173085,1335.9,150.0,False,0.0
148
- XGBoost,Tree-based,884.4808277373293,2.446295688771306,17.94866937141705,1340.9,201.0,False,0.0
149
- iLTM,Foundation Model,524.4765295003572,161.81343832142903,24.936616681743956,1075.2,1.0,False,0.0
150
- iLTM,Foundation Model,1309.3726391156104,315.1785851675429,21.999424996697776,1153.1,2.0,False,0.0
151
- iLTM,Foundation Model,3961.6553268098314,530.0687097749746,19.8928754460769,1262.6,5.0,False,0.0
152
- iLTM,Foundation Model,8841.914391496262,748.7519406764377,19.201295532808242,1314.2,10.0,False,0.0
153
- iLTM,Foundation Model,21737.971561100163,828.5067468065782,17.472125844838516,1374.1,25.0,False,0.0
154
- iLTM,Foundation Model,22811.744051243128,841.445890748085,17.446263555512726,1377.3,26.0,False,0.0
155
- xRFM,Other,5.446030178240368,1.0976028857093967,26.6617497580556,1050.7,1.0,False,0.0
156
- xRFM,Other,11.040462649383006,1.2469331487108437,24.011085249631414,1111.0,2.0,False,0.0
157
- xRFM,Other,27.95095076157401,1.484274975682999,20.58091874620733,1229.2,5.0,False,0.0
158
- xRFM,Other,55.941341031883795,1.8164722650517409,19.035002211287427,1299.2,10.0,False,0.0
159
- xRFM,Other,140.09100956098132,1.8569378725651828,18.243543201581396,1333.9,25.0,False,0.0
160
- xRFM,Other,280.2176062526802,2.3361472688801364,18.007335846730587,1344.6,50.0,False,0.0
161
- xRFM,Other,560.4680551302398,2.551869632074315,17.918464562812115,1346.4,100.0,False,0.0
162
- xRFM,Other,840.8353293444695,2.7950483190335644,17.90537995796114,1346.9,150.0,False,0.0
163
- xRFM,Other,1126.9686387245144,2.9237006693146794,17.894431738395728,1346.6,201.0,False,0.0