evaluma 0.2.0__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {evaluma-0.2.0 → evaluma-0.3.0}/.github/workflows/ci.yml +1 -13
- {evaluma-0.2.0 → evaluma-0.3.0}/PKG-INFO +69 -9
- {evaluma-0.2.0 → evaluma-0.3.0}/README.md +68 -8
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/overview.md +59 -4
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/references.md +27 -0
- evaluma-0.3.0/docs/tutorials/improvability.md +313 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/index.md +1 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/rank_sensitivity.md +7 -5
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/__init__.py +6 -3
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/_version.py +3 -3
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/benchmark.py +203 -9
- evaluma-0.3.0/evaluma/methods/improvability.py +88 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/rank_sensitivity.py +77 -17
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/metric_registry.py +65 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/plot.py +39 -4
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/results.py +39 -3
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/PKG-INFO +69 -9
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/SOURCES.txt +5 -0
- evaluma-0.3.0/evaluma.egg-info/scm_file_list.json +65 -0
- evaluma-0.3.0/evaluma.egg-info/scm_version.json +8 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_elo.py +4 -2
- evaluma-0.3.0/tests/test_improvability.py +281 -0
- evaluma-0.3.0/tests/test_metric_registry.py +129 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_rank_sensitivity.py +187 -1
- evaluma-0.2.0/tests/test_metric_registry.py +0 -63
- {evaluma-0.2.0 → evaluma-0.3.0}/.gitignore +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/.readthedocs.yaml +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/LICENSE +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/Makefile +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/_static/logo.png +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/conf.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/configuration.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/contributing.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/index.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/bayesian_comparison.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/elo_ranking.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/frequentist_comparison.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/frequentist_vs_bayesian.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/iqm_ranking.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/docs/tutorials/performance_profiles.md +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/cli.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/__init__.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/aggregate.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/bayesian.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/elo.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/frequentist.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/iqm.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/methods/profiles.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma/normalize.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/dependency_links.txt +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/entry_points.txt +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/requires.txt +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/evaluma.egg-info/top_level.txt +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/pyproject.toml +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/results_and_parameters.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/setup.cfg +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/conftest.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/fixtures/geobench_subset.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/fixtures/score_df.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/fixtures/score_df_lowerisbetter.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/fixtures/score_df_missing.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/fixtures/score_df_seeded.csv +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_aggregate.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_bayesian.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_benchmark.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_cli.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_frequentist.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_integration.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_iqm.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_load_df.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_load_metric_type_bounds.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_normalize.py +0 -0
- {evaluma-0.2.0 → evaluma-0.3.0}/tests/test_profiles.py +0 -0
|
@@ -34,21 +34,9 @@ jobs:
|
|
|
34
34
|
- run: ruff format --check .
|
|
35
35
|
- run: ty check
|
|
36
36
|
|
|
37
|
-
docs:
|
|
38
|
-
runs-on: ubuntu-latest
|
|
39
|
-
env:
|
|
40
|
-
IPYTHONDIR: /tmp/ipython
|
|
41
|
-
steps:
|
|
42
|
-
- uses: actions/checkout@v4
|
|
43
|
-
- uses: actions/setup-python@v5
|
|
44
|
-
with:
|
|
45
|
-
python-version: "3.11"
|
|
46
|
-
- run: pip install -e ".[docs]"
|
|
47
|
-
- run: sphinx-build -W -b html docs docs/_build/html
|
|
48
|
-
|
|
49
37
|
publish:
|
|
50
38
|
if: startsWith(github.ref, 'refs/tags/v')
|
|
51
|
-
needs: [test, lint
|
|
39
|
+
needs: [test, lint]
|
|
52
40
|
runs-on: ubuntu-latest
|
|
53
41
|
environment: pypi
|
|
54
42
|
permissions:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: evaluma
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: ML benchmark ranking tools — IQM, Bayesian pairwise, and Dolan-Moré performance profiles
|
|
5
5
|
Author-email: Nils Lehmann <nils.lehmann24@gmail.com>
|
|
6
6
|
License: Apache 2.0
|
|
@@ -58,11 +58,16 @@ Dynamic: license-file
|
|
|
58
58
|
|
|
59
59
|
# evaluma
|
|
60
60
|
|
|
61
|
-
A small Python package for comparing machine learning models across benchmark suites. Given a CSV of per-model, per-dataset scores, evaluma
|
|
61
|
+
A small Python package for comparing machine learning models across benchmark suites. Given a CSV of per-model, per-dataset scores, evaluma computes eight complementary views of the results:
|
|
62
62
|
|
|
63
|
+
- **Aggregate ranking** — point-estimate ranking via trimmed mean, mean, or median
|
|
63
64
|
- **IQM ranking** — interquartile mean with bootstrapped confidence intervals, following [Agarwal et al. (2021)](https://arxiv.org/abs/2108.13264)
|
|
65
|
+
- **ELO ranking** — MLE ELO ratings from pairwise head-to-head battles with bootstrap CIs and a win-rate matrix, following [Erickson et al. (2025)](https://arxiv.org/abs/2506.16791)
|
|
66
|
+
- **Improvability ranking** — mean percent error reduction each model needs to match the per-dataset best, in raw error space, following [TabArena](https://arxiv.org/abs/2506.16791) / [BeyondArena](https://arxiv.org/abs/2606.30410)
|
|
64
67
|
- **Bayesian pairwise comparison** — posterior probabilities that model A beats model B (or is practically equivalent), via [baycomp](https://github.com/janezd/baycomp)
|
|
65
|
-
- **
|
|
68
|
+
- **Frequentist comparison** — Friedman + Nemenyi (all-pairs) or Wilcoxon + Holm (reference model), following [Demšar (2006)](https://jmlr.org/papers/v7/demsar06a.html)
|
|
69
|
+
- **Dolan-Moré performance profiles** — cumulative distribution of performance ratios and area-under-profile scores, following [Dolan & Moré (2002)](https://doi.org/10.1007/s101070100263)
|
|
70
|
+
- **Rank sensitivity** — Kendall τ-b with bootstrap CI measuring whether rankings hold across two experimental conditions, following [Kendall (1945)](https://doi.org/10.1093/biomet/33.3.239)
|
|
66
71
|
|
|
67
72
|
|
|
68
73
|
## Documentation
|
|
@@ -99,36 +104,62 @@ bench = evaluma.load_df(
|
|
|
99
104
|
score="score",
|
|
100
105
|
)
|
|
101
106
|
|
|
102
|
-
#
|
|
107
|
+
# Point-estimate aggregate ranking (trimmed mean)
|
|
108
|
+
agg = bench.aggregate_ranking()
|
|
109
|
+
print(agg.table)
|
|
110
|
+
|
|
111
|
+
# IQM ranking with 95% bootstrap CI (requires seed column)
|
|
103
112
|
iqm = bench.iqm_ranking()
|
|
104
113
|
print(iqm.table)
|
|
105
114
|
fig = iqm.plot()
|
|
106
115
|
fig.savefig("iqm.png")
|
|
107
116
|
|
|
117
|
+
# ELO ranking with win-rate matrix
|
|
118
|
+
elo = bench.elo_ranking()
|
|
119
|
+
print(elo.table)
|
|
120
|
+
fig = elo.plot_winrate()
|
|
121
|
+
|
|
122
|
+
# Improvability ranking (mean % error reduction needed to match the best)
|
|
123
|
+
imp = bench.improvability_ranking()
|
|
124
|
+
print(imp.table)
|
|
125
|
+
fig = imp.plot()
|
|
126
|
+
|
|
108
127
|
# Bayesian pairwise probabilities
|
|
109
128
|
bayes = bench.bayesian_comparison()
|
|
110
129
|
print(bayes.table)
|
|
111
130
|
|
|
131
|
+
# Frequentist comparison (Friedman + Nemenyi)
|
|
132
|
+
freq = bench.frequentist_comparison()
|
|
133
|
+
print(freq.table)
|
|
134
|
+
|
|
112
135
|
# Dolan-Moré performance profiles
|
|
113
136
|
profiles = bench.performance_profiles()
|
|
114
137
|
fig = profiles.plot()
|
|
138
|
+
|
|
139
|
+
# Rank sensitivity across two conditions
|
|
140
|
+
bench_b = evaluma.load_df("results_b.csv", model="model", dataset="dataset",
|
|
141
|
+
metric="metric", score="score")
|
|
142
|
+
sens = bench.rank_sensitivity(bench_b, cond_a="condA", cond_b="condB")
|
|
143
|
+
print(f"Kendall τ = {sens.tau:.3f}, 95% CI = {sens.tau_ci}")
|
|
115
144
|
```
|
|
116
145
|
|
|
117
146
|
### CLI
|
|
118
147
|
|
|
119
148
|
```bash
|
|
120
|
-
# Run
|
|
149
|
+
# Run aggregate, Bayesian, frequentist, and performance-profile analyses
|
|
121
150
|
evaluma report results.csv \
|
|
122
151
|
--model model --dataset dataset --metric metric --score score \
|
|
123
152
|
--output results/
|
|
124
153
|
|
|
125
154
|
# Individual subcommands
|
|
126
|
-
evaluma
|
|
127
|
-
evaluma
|
|
128
|
-
evaluma
|
|
155
|
+
evaluma aggregate results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
156
|
+
evaluma rank results.csv --model model --dataset dataset --metric metric --score score --seed seed --output results/
|
|
157
|
+
evaluma compare results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
158
|
+
evaluma frequentist results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
159
|
+
evaluma profiles results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
129
160
|
```
|
|
130
161
|
|
|
131
|
-
Each subcommand writes a `.csv` table and a `.png` figure to `--output`.
|
|
162
|
+
Each subcommand writes a `.csv` table and a `.png` figure to `--output`. ELO ranking and rank sensitivity are available via the Python API only.
|
|
132
163
|
|
|
133
164
|
### Column mapping
|
|
134
165
|
|
|
@@ -238,6 +269,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
238
269
|
year = {2021},
|
|
239
270
|
}
|
|
240
271
|
|
|
272
|
+
@inproceedings{erickson2025tabarena,
|
|
273
|
+
title = {{TabArena}: A Living Benchmark for Machine Learning on Tabular Data},
|
|
274
|
+
author = {Erickson, Nick and Purucker, Lennart and Tschalzev, Andrej
|
|
275
|
+
and Holzm{\"u}ller, David and Mutalik Desai, Prabhant
|
|
276
|
+
and Salinas, David and Hutter, Frank},
|
|
277
|
+
booktitle = {Advances in Neural Information Processing Systems},
|
|
278
|
+
year = {2025},
|
|
279
|
+
}
|
|
280
|
+
|
|
241
281
|
@article{benavoli2017time,
|
|
242
282
|
title = {Time for a Change: a Tutorial for Comparing Multiple Classifiers
|
|
243
283
|
Through Bayesian Analysis},
|
|
@@ -250,6 +290,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
250
290
|
year = {2017},
|
|
251
291
|
}
|
|
252
292
|
|
|
293
|
+
@article{demsar2006statistical,
|
|
294
|
+
title = {Statistical Comparisons of Classifiers over Multiple Data Sets},
|
|
295
|
+
author = {Dem{\v{s}}ar, Janez},
|
|
296
|
+
journal = {Journal of Machine Learning Research},
|
|
297
|
+
volume = {7},
|
|
298
|
+
pages = {1--30},
|
|
299
|
+
year = {2006},
|
|
300
|
+
}
|
|
301
|
+
|
|
253
302
|
@article{dolan2002benchmarking,
|
|
254
303
|
title = {Benchmarking Optimization Software with Performance Profiles},
|
|
255
304
|
author = {Dolan, Elizabeth D. and Mor{\'e}, Jorge J.},
|
|
@@ -258,4 +307,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
258
307
|
pages = {201--213},
|
|
259
308
|
year = {2002},
|
|
260
309
|
}
|
|
310
|
+
|
|
311
|
+
@article{kendall1945treatment,
|
|
312
|
+
title = {The Treatment of Ties in Ranking Problems},
|
|
313
|
+
author = {Kendall, Maurice G.},
|
|
314
|
+
journal = {Biometrika},
|
|
315
|
+
volume = {33},
|
|
316
|
+
number = {3},
|
|
317
|
+
pages = {239--251},
|
|
318
|
+
year = {1945},
|
|
319
|
+
doi = {10.1093/biomet/33.3.239},
|
|
320
|
+
}
|
|
261
321
|
```
|
|
@@ -13,11 +13,16 @@
|
|
|
13
13
|
|
|
14
14
|
# evaluma
|
|
15
15
|
|
|
16
|
-
A small Python package for comparing machine learning models across benchmark suites. Given a CSV of per-model, per-dataset scores, evaluma
|
|
16
|
+
A small Python package for comparing machine learning models across benchmark suites. Given a CSV of per-model, per-dataset scores, evaluma computes eight complementary views of the results:
|
|
17
17
|
|
|
18
|
+
- **Aggregate ranking** — point-estimate ranking via trimmed mean, mean, or median
|
|
18
19
|
- **IQM ranking** — interquartile mean with bootstrapped confidence intervals, following [Agarwal et al. (2021)](https://arxiv.org/abs/2108.13264)
|
|
20
|
+
- **ELO ranking** — MLE ELO ratings from pairwise head-to-head battles with bootstrap CIs and a win-rate matrix, following [Erickson et al. (2025)](https://arxiv.org/abs/2506.16791)
|
|
21
|
+
- **Improvability ranking** — mean percent error reduction each model needs to match the per-dataset best, in raw error space, following [TabArena](https://arxiv.org/abs/2506.16791) / [BeyondArena](https://arxiv.org/abs/2606.30410)
|
|
19
22
|
- **Bayesian pairwise comparison** — posterior probabilities that model A beats model B (or is practically equivalent), via [baycomp](https://github.com/janezd/baycomp)
|
|
20
|
-
- **
|
|
23
|
+
- **Frequentist comparison** — Friedman + Nemenyi (all-pairs) or Wilcoxon + Holm (reference model), following [Demšar (2006)](https://jmlr.org/papers/v7/demsar06a.html)
|
|
24
|
+
- **Dolan-Moré performance profiles** — cumulative distribution of performance ratios and area-under-profile scores, following [Dolan & Moré (2002)](https://doi.org/10.1007/s101070100263)
|
|
25
|
+
- **Rank sensitivity** — Kendall τ-b with bootstrap CI measuring whether rankings hold across two experimental conditions, following [Kendall (1945)](https://doi.org/10.1093/biomet/33.3.239)
|
|
21
26
|
|
|
22
27
|
|
|
23
28
|
## Documentation
|
|
@@ -54,36 +59,62 @@ bench = evaluma.load_df(
|
|
|
54
59
|
score="score",
|
|
55
60
|
)
|
|
56
61
|
|
|
57
|
-
#
|
|
62
|
+
# Point-estimate aggregate ranking (trimmed mean)
|
|
63
|
+
agg = bench.aggregate_ranking()
|
|
64
|
+
print(agg.table)
|
|
65
|
+
|
|
66
|
+
# IQM ranking with 95% bootstrap CI (requires seed column)
|
|
58
67
|
iqm = bench.iqm_ranking()
|
|
59
68
|
print(iqm.table)
|
|
60
69
|
fig = iqm.plot()
|
|
61
70
|
fig.savefig("iqm.png")
|
|
62
71
|
|
|
72
|
+
# ELO ranking with win-rate matrix
|
|
73
|
+
elo = bench.elo_ranking()
|
|
74
|
+
print(elo.table)
|
|
75
|
+
fig = elo.plot_winrate()
|
|
76
|
+
|
|
77
|
+
# Improvability ranking (mean % error reduction needed to match the best)
|
|
78
|
+
imp = bench.improvability_ranking()
|
|
79
|
+
print(imp.table)
|
|
80
|
+
fig = imp.plot()
|
|
81
|
+
|
|
63
82
|
# Bayesian pairwise probabilities
|
|
64
83
|
bayes = bench.bayesian_comparison()
|
|
65
84
|
print(bayes.table)
|
|
66
85
|
|
|
86
|
+
# Frequentist comparison (Friedman + Nemenyi)
|
|
87
|
+
freq = bench.frequentist_comparison()
|
|
88
|
+
print(freq.table)
|
|
89
|
+
|
|
67
90
|
# Dolan-Moré performance profiles
|
|
68
91
|
profiles = bench.performance_profiles()
|
|
69
92
|
fig = profiles.plot()
|
|
93
|
+
|
|
94
|
+
# Rank sensitivity across two conditions
|
|
95
|
+
bench_b = evaluma.load_df("results_b.csv", model="model", dataset="dataset",
|
|
96
|
+
metric="metric", score="score")
|
|
97
|
+
sens = bench.rank_sensitivity(bench_b, cond_a="condA", cond_b="condB")
|
|
98
|
+
print(f"Kendall τ = {sens.tau:.3f}, 95% CI = {sens.tau_ci}")
|
|
70
99
|
```
|
|
71
100
|
|
|
72
101
|
### CLI
|
|
73
102
|
|
|
74
103
|
```bash
|
|
75
|
-
# Run
|
|
104
|
+
# Run aggregate, Bayesian, frequentist, and performance-profile analyses
|
|
76
105
|
evaluma report results.csv \
|
|
77
106
|
--model model --dataset dataset --metric metric --score score \
|
|
78
107
|
--output results/
|
|
79
108
|
|
|
80
109
|
# Individual subcommands
|
|
81
|
-
evaluma
|
|
82
|
-
evaluma
|
|
83
|
-
evaluma
|
|
110
|
+
evaluma aggregate results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
111
|
+
evaluma rank results.csv --model model --dataset dataset --metric metric --score score --seed seed --output results/
|
|
112
|
+
evaluma compare results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
113
|
+
evaluma frequentist results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
114
|
+
evaluma profiles results.csv --model model --dataset dataset --metric metric --score score --output results/
|
|
84
115
|
```
|
|
85
116
|
|
|
86
|
-
Each subcommand writes a `.csv` table and a `.png` figure to `--output`.
|
|
117
|
+
Each subcommand writes a `.csv` table and a `.png` figure to `--output`. ELO ranking and rank sensitivity are available via the Python API only.
|
|
87
118
|
|
|
88
119
|
### Column mapping
|
|
89
120
|
|
|
@@ -193,6 +224,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
193
224
|
year = {2021},
|
|
194
225
|
}
|
|
195
226
|
|
|
227
|
+
@inproceedings{erickson2025tabarena,
|
|
228
|
+
title = {{TabArena}: A Living Benchmark for Machine Learning on Tabular Data},
|
|
229
|
+
author = {Erickson, Nick and Purucker, Lennart and Tschalzev, Andrej
|
|
230
|
+
and Holzm{\"u}ller, David and Mutalik Desai, Prabhant
|
|
231
|
+
and Salinas, David and Hutter, Frank},
|
|
232
|
+
booktitle = {Advances in Neural Information Processing Systems},
|
|
233
|
+
year = {2025},
|
|
234
|
+
}
|
|
235
|
+
|
|
196
236
|
@article{benavoli2017time,
|
|
197
237
|
title = {Time for a Change: a Tutorial for Comparing Multiple Classifiers
|
|
198
238
|
Through Bayesian Analysis},
|
|
@@ -205,6 +245,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
205
245
|
year = {2017},
|
|
206
246
|
}
|
|
207
247
|
|
|
248
|
+
@article{demsar2006statistical,
|
|
249
|
+
title = {Statistical Comparisons of Classifiers over Multiple Data Sets},
|
|
250
|
+
author = {Dem{\v{s}}ar, Janez},
|
|
251
|
+
journal = {Journal of Machine Learning Research},
|
|
252
|
+
volume = {7},
|
|
253
|
+
pages = {1--30},
|
|
254
|
+
year = {2006},
|
|
255
|
+
}
|
|
256
|
+
|
|
208
257
|
@article{dolan2002benchmarking,
|
|
209
258
|
title = {Benchmarking Optimization Software with Performance Profiles},
|
|
210
259
|
author = {Dolan, Elizabeth D. and Mor{\'e}, Jorge J.},
|
|
@@ -213,4 +262,15 @@ also cite the works of the underlying methods and frameworks used:
|
|
|
213
262
|
pages = {201--213},
|
|
214
263
|
year = {2002},
|
|
215
264
|
}
|
|
265
|
+
|
|
266
|
+
@article{kendall1945treatment,
|
|
267
|
+
title = {The Treatment of Ties in Ranking Problems},
|
|
268
|
+
author = {Kendall, Maurice G.},
|
|
269
|
+
journal = {Biometrika},
|
|
270
|
+
volume = {33},
|
|
271
|
+
number = {3},
|
|
272
|
+
pages = {239--251},
|
|
273
|
+
year = {1945},
|
|
274
|
+
doi = {10.1093/biomet/33.3.239},
|
|
275
|
+
}
|
|
216
276
|
```
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
# Overview
|
|
2
2
|
|
|
3
|
-
Given a collection of models evaluated on multiple datasets, the question "which model is best?" is harder than it sounds. Simple averages obscure robustness, ignore statistical uncertainty, and make it difficult to compare methods across papers. However, several works in the literature and different competition benchmarks have explored various schemas to come up with informative rankings. **evaluma** is designed to be an accessible entry point to build such rankings for your benchmark evaluations providing
|
|
3
|
+
Given a collection of models evaluated on multiple datasets, the question "which model is best?" is harder than it sounds. Simple averages obscure robustness, ignore statistical uncertainty, and make it difficult to compare methods across papers. However, several works in the literature and different competition benchmarks have explored various schemas to come up with informative rankings. **evaluma** is designed to be an accessible entry point to build such rankings for your benchmark evaluations providing six complementary analyses from a single input format:
|
|
4
4
|
|
|
5
5
|
- **Aggregate ranking** — point-estimate ranking via trimmed mean, mean, or median
|
|
6
6
|
- **IQM ranking** — robust central tendency with bootstrap confidence intervals (requires multiple seeds per model/dataset)
|
|
7
|
+
- **ELO ranking** — MLE ELO ratings from pairwise head-to-head battles with bootstrap CIs and a win-rate matrix
|
|
7
8
|
- **Bayesian comparison** — probability that one model outperforms another
|
|
9
|
+
- **Frequentist comparison** — Friedman + Nemenyi (all-pairs) or Wilcoxon + Holm (reference model) significance tests
|
|
8
10
|
- **Performance profiles** — cumulative distribution showing how often each model comes within a factor of the best
|
|
11
|
+
- **Rank sensitivity** — Kendall τ-b measuring whether rankings hold across two experimental conditions
|
|
9
12
|
|
|
10
13
|
This page walks through installing the package, preparing your results CSV, and running the analyses.
|
|
11
14
|
|
|
@@ -65,7 +68,7 @@ bench = evaluma.load_csv(
|
|
|
65
68
|
)
|
|
66
69
|
```
|
|
67
70
|
|
|
68
|
-
The `Benchmark` object holds your data and provides
|
|
71
|
+
The `Benchmark` object holds your data and provides analysis methods. Each returns a result object with a `.table` (pandas DataFrame) and a `.plot()` method:
|
|
69
72
|
|
|
70
73
|
### Aggregate ranking
|
|
71
74
|
|
|
@@ -91,6 +94,19 @@ iqm.plot()
|
|
|
91
94
|
|
|
92
95
|
The IQM discards the top and bottom 25% of per-dataset scores before averaging, making it resistant to outliers. The bootstrap CIs are stratified — seeds are resampled independently within each dataset. **Requires multiple seeds** — pass `seed="seed_column"` to `load_csv()` when loading. Use `aggregate_ranking()` for single-run data. See the [IQM Tutorial](./tutorials/iqm_ranking.md) for a more in-depth example.
|
|
93
96
|
|
|
97
|
+
### ELO ranking
|
|
98
|
+
|
|
99
|
+
Compute MLE ELO ratings from pairwise head-to-head battles across datasets:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
elo = bench.elo_ranking()
|
|
103
|
+
print(elo.table) # model, ELO, CI_low, CI_high
|
|
104
|
+
elo.plot() # horizontal bar chart with 95% CI
|
|
105
|
+
elo.plot_winrate() # M×M win-rate heatmap
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Each dataset contributes equally via sample weighting. Bootstrap CIs resample battles within each dataset. `tie_threshold` treats near-equal scores as draws; `calibration_model` anchors one model to ELO 1000. See the [ELO Ranking Tutorial](./tutorials/elo_ranking.md) for a more in-depth example.
|
|
109
|
+
|
|
94
110
|
### Bayesian pairwise comparison
|
|
95
111
|
|
|
96
112
|
Compute posterior probabilities for every model pair:
|
|
@@ -103,6 +119,22 @@ bayes.plot()
|
|
|
103
119
|
|
|
104
120
|
For each pair (A, B), the output gives `p_a_better`, `p_equiv`, and `p_b_better` — the probabilities that A is better, equivalent (within a practical equivalence region), or worse than B. See the [Bayesian Comparison](./tutorials/bayesian_comparison.md) for a more in-depth example.
|
|
105
121
|
|
|
122
|
+
### Frequentist comparison
|
|
123
|
+
|
|
124
|
+
Run Friedman + Nemenyi (all-pairs) or Wilcoxon + Holm (reference model) significance tests:
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
# All-pairs: Friedman omnibus then Nemenyi post-hoc
|
|
128
|
+
freq = bench.frequentist_comparison()
|
|
129
|
+
print(freq.table)
|
|
130
|
+
freq.plot() # critical-difference diagram
|
|
131
|
+
|
|
132
|
+
# Reference mode: Wilcoxon + Holm against one baseline
|
|
133
|
+
freq_ref = bench.frequentist_comparison(reference="ModelA")
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Requires at least 5 datasets. See the [Frequentist Comparison Tutorial](./tutorials/frequentist_comparison.md) for a more in-depth example.
|
|
137
|
+
|
|
106
138
|
### Performance profiles
|
|
107
139
|
|
|
108
140
|
Plot how often each model achieves near-best performance across datasets:
|
|
@@ -115,6 +147,24 @@ profiles.plot()
|
|
|
115
147
|
|
|
116
148
|
The profile curve shows, for each performance ratio τ ≥ 1, the fraction of datasets where a model's score is within τ of the best. A curve that rises faster means the model is closer to best more often. See the [Performance Profile Tutorial](./tutorials/performance_profiles.md) for a more in-depth example.
|
|
117
149
|
|
|
150
|
+
### Rank sensitivity
|
|
151
|
+
|
|
152
|
+
Measure whether model rankings hold when you change an experimental condition (e.g. optimizer, augmentation policy, normalization choice):
|
|
153
|
+
|
|
154
|
+
```python
|
|
155
|
+
from evaluma import load_csv
|
|
156
|
+
|
|
157
|
+
# Load two conditions separately, or use condition_col to split a single DataFrame
|
|
158
|
+
bench_b = load_csv("results_b.csv", model="model", dataset="dataset",
|
|
159
|
+
metric="metric", score="score")
|
|
160
|
+
sens = bench.rank_sensitivity(bench_b, cond_a="condA", cond_b="condB")
|
|
161
|
+
print(f"Kendall τ = {sens.tau:.3f}, 95% CI = {sens.tau_ci}")
|
|
162
|
+
print(sens.table) # per-model delta_rank, sorted by |delta_rank|
|
|
163
|
+
sens.plot() # rank scatter with identity line
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Alternatively, pass `condition_col=` to `load_df` to get a `BenchmarkGroup` and call `.rank_sensitivity("condA", "condB")` directly. See the [Rank Sensitivity Tutorial](./tutorials/rank_sensitivity.md) for a more in-depth example.
|
|
167
|
+
|
|
118
168
|
## Column mapping
|
|
119
169
|
|
|
120
170
|
If your CSV uses different column names, pass them explicitly:
|
|
@@ -131,7 +181,7 @@ bench = evaluma.load_csv(
|
|
|
131
181
|
|
|
132
182
|
## CLI quickstart
|
|
133
183
|
|
|
134
|
-
Run
|
|
184
|
+
Run aggregate, Bayesian, frequentist, and performance-profile analyses, writing CSV and PNG outputs:
|
|
135
185
|
|
|
136
186
|
```bash
|
|
137
187
|
evaluma report results.csv
|
|
@@ -149,10 +199,15 @@ Save outputs to a specific directory:
|
|
|
149
199
|
evaluma report results.csv --output ./results/
|
|
150
200
|
```
|
|
151
201
|
|
|
152
|
-
Individual subcommands are also available: `evaluma rank` (IQM
|
|
202
|
+
Individual subcommands are also available: `evaluma aggregate`, `evaluma rank` (IQM, requires `--seed`), `evaluma compare` (Bayesian), `evaluma frequentist`, and `evaluma profiles`. The CLI supports the same column mapping with `--model`, `--dataset`, `--metric`, and `--score` flags, or via a YAML config file passed with `--config`.
|
|
203
|
+
|
|
204
|
+
ELO ranking and rank sensitivity are available via the Python API only.
|
|
153
205
|
|
|
154
206
|
## Next steps
|
|
155
207
|
|
|
156
208
|
- [IQM Ranking tutorial](tutorials/iqm_ranking) — deeper dive with worked examples
|
|
209
|
+
- [ELO Ranking tutorial](tutorials/elo_ranking) — head-to-head battles, win-rate matrix, and calibration
|
|
157
210
|
- [Bayesian Comparison tutorial](tutorials/bayesian_comparison) — interpreting posterior probabilities
|
|
211
|
+
- [Frequentist Comparison tutorial](tutorials/frequentist_comparison) — Friedman, Nemenyi, Wilcoxon, and CD diagrams
|
|
158
212
|
- [Performance Profiles tutorial](tutorials/performance_profiles) — understanding the Dolan-Moré framework
|
|
213
|
+
- [Rank Sensitivity tutorial](tutorials/rank_sensitivity) — measuring ranking stability across conditions
|
|
@@ -11,6 +11,15 @@ Deep Reinforcement Learning at the Edge of the Statistical Precipice.
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
+
**ELO Ranking**
|
|
15
|
+
|
|
16
|
+
Erickson, N., Purucker, L., Tschalzev, A., Holzmüller, D., Mutalik Desai, P., Salinas, D., & Hutter, F. (2025).
|
|
17
|
+
TabArena: A Living Benchmark for Machine Learning on Tabular Data.
|
|
18
|
+
*Advances in Neural Information Processing Systems* (Datasets and Benchmarks, Spotlight).
|
|
19
|
+
<https://arxiv.org/abs/2506.16791>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
14
23
|
**Bayesian Pairwise Comparison**
|
|
15
24
|
|
|
16
25
|
Benavoli, A., Corani, G., Demšar, J., & Zaffalon, M. (2017).
|
|
@@ -20,6 +29,15 @@ Time for a Change: a Tutorial for Comparing Multiple Classifiers Through Bayesia
|
|
|
20
29
|
|
|
21
30
|
---
|
|
22
31
|
|
|
32
|
+
**Frequentist Comparison**
|
|
33
|
+
|
|
34
|
+
Demšar, J. (2006).
|
|
35
|
+
Statistical Comparisons of Classifiers over Multiple Data Sets.
|
|
36
|
+
*Journal of Machine Learning Research*, 7, 1–30.
|
|
37
|
+
<https://jmlr.org/papers/v7/demsar06a.html>
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
23
41
|
**Dolan-Moré Performance Profiles**
|
|
24
42
|
|
|
25
43
|
Dolan, E. D., & Moré, J. J. (2002).
|
|
@@ -29,6 +47,15 @@ Benchmarking Optimization Software with Performance Profiles.
|
|
|
29
47
|
|
|
30
48
|
---
|
|
31
49
|
|
|
50
|
+
**Rank Sensitivity**
|
|
51
|
+
|
|
52
|
+
Kendall, M. G. (1945).
|
|
53
|
+
The Treatment of Ties in Ranking Problems.
|
|
54
|
+
*Biometrika*, 33(3), 239–251.
|
|
55
|
+
<https://doi.org/10.1093/biomet/33.3.239>
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
32
59
|
## Libraries
|
|
33
60
|
|
|
34
61
|
**baycomp**
|