evaluma 0.1.0__py3-none-any.whl
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/__init__.py +258 -0
- evaluma/_version.py +24 -0
- evaluma/benchmark.py +211 -0
- evaluma/cli.py +275 -0
- evaluma/methods/__init__.py +0 -0
- evaluma/methods/aggregate.py +44 -0
- evaluma/methods/bayesian.py +64 -0
- evaluma/methods/iqm.py +93 -0
- evaluma/methods/profiles.py +72 -0
- evaluma/metric_registry.py +69 -0
- evaluma/normalize.py +84 -0
- evaluma/plot.py +274 -0
- evaluma/results.py +173 -0
- evaluma-0.1.0.dist-info/METADATA +259 -0
- evaluma-0.1.0.dist-info/RECORD +19 -0
- evaluma-0.1.0.dist-info/WHEEL +5 -0
- evaluma-0.1.0.dist-info/entry_points.txt +2 -0
- evaluma-0.1.0.dist-info/licenses/LICENSE +201 -0
- evaluma-0.1.0.dist-info/top_level.txt +1 -0
evaluma/__init__.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from evaluma._version import __version__ # noqa: F401
|
|
4
|
+
from evaluma.benchmark import Benchmark # noqa: F401
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def load_df(
|
|
8
|
+
df,
|
|
9
|
+
*,
|
|
10
|
+
model="model",
|
|
11
|
+
dataset="dataset",
|
|
12
|
+
metric="metric",
|
|
13
|
+
score="score",
|
|
14
|
+
seed=None,
|
|
15
|
+
metric_type_bounds=None,
|
|
16
|
+
norm_ref_low=None,
|
|
17
|
+
norm_ref_high=None,
|
|
18
|
+
metric_direction=None,
|
|
19
|
+
drop_incomplete=False,
|
|
20
|
+
):
|
|
21
|
+
"""Load a DataFrame and return a ready-to-use Benchmark object.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
df: A pandas DataFrame in long format (one row per model/dataset pair).
|
|
25
|
+
To load from a CSV file, use :func:`evaluma.load_csv` instead.
|
|
26
|
+
model: Column name for the model identifier.
|
|
27
|
+
dataset: Column name for the dataset identifier.
|
|
28
|
+
metric: Column name for the metric identifier.
|
|
29
|
+
score: Column name for the score values.
|
|
30
|
+
seed: Column name for the random seed. When provided, all seed rows
|
|
31
|
+
are preserved and a ``seed`` column is included in the loaded
|
|
32
|
+
DataFrame.
|
|
33
|
+
metric_type_bounds: Dict mapping metric names to ``(low, high)``
|
|
34
|
+
bound tuples. ``high`` may be a model name string, resolved
|
|
35
|
+
per-dataset to that model's score. When provided, ``norm_ref_low``
|
|
36
|
+
and ``norm_ref_high`` must be ``None``. Metric direction is inferred
|
|
37
|
+
from the built-in registry; unknown metrics raise ``ValueError``.
|
|
38
|
+
Bounded metrics not listed here (e.g. accuracy, iou, f1) use their
|
|
39
|
+
natural ``[0, 1]`` bounds automatically. Unbounded metrics (rmse,
|
|
40
|
+
mae, mse) must be listed; omitting them raises ``ValueError``.
|
|
41
|
+
norm_ref_low: Lower normalization reference — scalar, model name,
|
|
42
|
+
or per-dataset dict. If ``None``, the per-dataset minimum is
|
|
43
|
+
used and a ``UserWarning`` is emitted. Cannot be combined with
|
|
44
|
+
``metric_type_bounds``.
|
|
45
|
+
norm_ref_high: Upper normalization reference, same format as
|
|
46
|
+
``norm_ref_low``. If ``None``, the per-dataset maximum is used.
|
|
47
|
+
Cannot be combined with ``metric_type_bounds``.
|
|
48
|
+
metric_direction: Dict mapping dataset names to ``"min"`` or
|
|
49
|
+
``"max"``. When used with ``metric_type_bounds``, these entries
|
|
50
|
+
take precedence over the registry-inferred direction. Without
|
|
51
|
+
``metric_type_bounds``, datasets mapped to ``"min"`` are negated
|
|
52
|
+
before normalization so that higher is always better.
|
|
53
|
+
drop_incomplete: If ``True``, silently drop models with missing
|
|
54
|
+
scores instead of raising.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
Benchmark: Normalized benchmark ready for analysis.
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
TypeError: If ``df`` is not a pandas DataFrame.
|
|
61
|
+
ValueError: If ``metric_type_bounds`` is provided together with
|
|
62
|
+
``norm_ref_low`` or ``norm_ref_high``.
|
|
63
|
+
ValueError: If the data contains more than one metric per
|
|
64
|
+
(model, dataset) pair, or if the score matrix is incomplete
|
|
65
|
+
and ``drop_incomplete`` is ``False``.
|
|
66
|
+
ValueError: If a metric referenced by a dataset is not in the
|
|
67
|
+
registry and not covered by ``metric_type_bounds``.
|
|
68
|
+
ValueError: If a regression metric (rmse, mae, mse) is present but
|
|
69
|
+
no upper bound is specified in ``metric_type_bounds``.
|
|
70
|
+
"""
|
|
71
|
+
if not isinstance(df, pd.DataFrame):
|
|
72
|
+
raise TypeError(
|
|
73
|
+
f"load_df() expects a pandas DataFrame, got {type(df).__name__}. "
|
|
74
|
+
"To load from a file, use evaluma.load_csv()."
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if metric_type_bounds is not None and (
|
|
78
|
+
norm_ref_low is not None or norm_ref_high is not None
|
|
79
|
+
):
|
|
80
|
+
raise ValueError(
|
|
81
|
+
"metric_type_bounds cannot be combined with norm_ref_low or norm_ref_high. "
|
|
82
|
+
"Use metric_type_bounds to specify all bounds, "
|
|
83
|
+
"or use norm_ref_low/norm_ref_high."
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
df = df.copy()
|
|
87
|
+
rename = {model: "model", dataset: "dataset", metric: "metric", score: "score"}
|
|
88
|
+
if seed is not None:
|
|
89
|
+
rename[seed] = "seed"
|
|
90
|
+
df = df.rename(columns=rename)
|
|
91
|
+
|
|
92
|
+
metrics_per_dataset = df.groupby(["model", "dataset"])["metric"].nunique()
|
|
93
|
+
if (metrics_per_dataset > 1).any():
|
|
94
|
+
raise ValueError("more than one metric found per (model, dataset) cell")
|
|
95
|
+
|
|
96
|
+
check_df = df.drop_duplicates(subset=["model", "dataset"])
|
|
97
|
+
pivot = check_df.pivot(index="model", columns="dataset", values="score")
|
|
98
|
+
missing = pivot.isna()
|
|
99
|
+
if missing.to_numpy().any():
|
|
100
|
+
missing_cells = [
|
|
101
|
+
(m, d)
|
|
102
|
+
for m in pivot.index
|
|
103
|
+
for d in pivot.columns
|
|
104
|
+
if pd.isna(pivot.loc[m, d])
|
|
105
|
+
]
|
|
106
|
+
if drop_incomplete:
|
|
107
|
+
complete_models = pivot.index[~missing.any(axis=1)]
|
|
108
|
+
df = df[df["model"].isin(complete_models)].reset_index(drop=True)
|
|
109
|
+
else:
|
|
110
|
+
cell_str = ", ".join(f"({m}, {d})" for m, d in missing_cells)
|
|
111
|
+
raise ValueError(f"Incomplete score matrix: missing cells {cell_str}")
|
|
112
|
+
|
|
113
|
+
if seed is not None:
|
|
114
|
+
df = df[["model", "dataset", "metric", "score", "seed"]]
|
|
115
|
+
else:
|
|
116
|
+
df = df[["model", "dataset", "metric", "score"]]
|
|
117
|
+
|
|
118
|
+
raw_runs = None
|
|
119
|
+
if seed is not None:
|
|
120
|
+
raw_runs = df[["model", "dataset", "seed", "score"]].reset_index(drop=True)
|
|
121
|
+
raw_matrix = (
|
|
122
|
+
df.groupby(["model", "dataset"], as_index=False)["score"]
|
|
123
|
+
.mean()
|
|
124
|
+
.pivot(index="model", columns="dataset", values="score")
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
raw_matrix = df.pivot(index="model", columns="dataset", values="score")
|
|
128
|
+
raw_matrix.columns.name = None
|
|
129
|
+
|
|
130
|
+
if metric_type_bounds is not None:
|
|
131
|
+
dataset_metric_map = (
|
|
132
|
+
df.drop_duplicates("dataset").set_index("dataset")["metric"].to_dict()
|
|
133
|
+
)
|
|
134
|
+
norm_ref_low, norm_ref_high, metric_direction = _resolve_metric_type_bounds(
|
|
135
|
+
metric_type_bounds, dataset_metric_map, raw_matrix, metric_direction
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
return Benchmark(
|
|
139
|
+
raw_matrix,
|
|
140
|
+
norm_ref_low=norm_ref_low,
|
|
141
|
+
norm_ref_high=norm_ref_high,
|
|
142
|
+
metric_direction=metric_direction,
|
|
143
|
+
raw_runs=raw_runs,
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def load_csv(
|
|
148
|
+
path,
|
|
149
|
+
*,
|
|
150
|
+
model="model",
|
|
151
|
+
dataset="dataset",
|
|
152
|
+
metric="metric",
|
|
153
|
+
score="score",
|
|
154
|
+
seed=None,
|
|
155
|
+
metric_type_bounds=None,
|
|
156
|
+
norm_ref_low=None,
|
|
157
|
+
norm_ref_high=None,
|
|
158
|
+
metric_direction=None,
|
|
159
|
+
drop_incomplete=False,
|
|
160
|
+
):
|
|
161
|
+
"""Load a benchmark CSV file and return a ready-to-use Benchmark object.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
path: Path to the CSV file.
|
|
165
|
+
model: Column name for the model identifier.
|
|
166
|
+
dataset: Column name for the dataset identifier.
|
|
167
|
+
metric: Column name for the metric identifier.
|
|
168
|
+
score: Column name for the score values.
|
|
169
|
+
seed: Column name for the random seed.
|
|
170
|
+
metric_type_bounds: See :func:`evaluma.load_df`.
|
|
171
|
+
norm_ref_low: See :func:`evaluma.load_df`.
|
|
172
|
+
norm_ref_high: See :func:`evaluma.load_df`.
|
|
173
|
+
metric_direction: See :func:`evaluma.load_df`.
|
|
174
|
+
drop_incomplete: See :func:`evaluma.load_df`.
|
|
175
|
+
|
|
176
|
+
Returns:
|
|
177
|
+
Benchmark: Normalized benchmark ready for analysis.
|
|
178
|
+
"""
|
|
179
|
+
df = pd.read_csv(path)
|
|
180
|
+
return load_df(
|
|
181
|
+
df,
|
|
182
|
+
model=model,
|
|
183
|
+
dataset=dataset,
|
|
184
|
+
metric=metric,
|
|
185
|
+
score=score,
|
|
186
|
+
seed=seed,
|
|
187
|
+
metric_type_bounds=metric_type_bounds,
|
|
188
|
+
norm_ref_low=norm_ref_low,
|
|
189
|
+
norm_ref_high=norm_ref_high,
|
|
190
|
+
metric_direction=metric_direction,
|
|
191
|
+
drop_incomplete=drop_incomplete,
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _resolve_metric_type_bounds(
|
|
196
|
+
metric_type_bounds, dataset_metric_map, raw_matrix, metric_direction_override
|
|
197
|
+
):
|
|
198
|
+
"""Resolve per-dataset normalization bounds and directions from the metric registry.
|
|
199
|
+
|
|
200
|
+
For each dataset, consults ``metric_type_bounds`` first, then falls back to the
|
|
201
|
+
built-in registry for metrics with natural bounds. Raises if an unbounded metric
|
|
202
|
+
(rmse, mae, mse) has no entry in ``metric_type_bounds``.
|
|
203
|
+
|
|
204
|
+
Args:
|
|
205
|
+
metric_type_bounds: Dict mapping metric names → ``(low, high)`` tuples.
|
|
206
|
+
dataset_metric_map: Dict mapping dataset names → metric name strings.
|
|
207
|
+
raw_matrix: Model × dataset score DataFrame (used to resolve model-name bounds).
|
|
208
|
+
metric_direction_override: Optional dict mapping dataset names →
|
|
209
|
+
``"min"``/``"max"``; these entries override registry-inferred directions.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
tuple: ``(norm_ref_low, norm_ref_high, metric_direction)`` where the first
|
|
213
|
+
two are
|
|
214
|
+
``pd.Series`` keyed by dataset and the last is a dict (or ``None`` if empty).
|
|
215
|
+
"""
|
|
216
|
+
from evaluma.metric_registry import get_direction, get_natural_bounds
|
|
217
|
+
|
|
218
|
+
mtb_lower = {k.lower(): v for k, v in metric_type_bounds.items()}
|
|
219
|
+
|
|
220
|
+
low_dict = {}
|
|
221
|
+
high_dict = {}
|
|
222
|
+
direction_dict = {}
|
|
223
|
+
|
|
224
|
+
for dataset, metric_name in dataset_metric_map.items():
|
|
225
|
+
metric_lower = metric_name.lower()
|
|
226
|
+
|
|
227
|
+
if metric_lower in mtb_lower:
|
|
228
|
+
low, high = mtb_lower[metric_lower]
|
|
229
|
+
if isinstance(high, str):
|
|
230
|
+
model_name = high
|
|
231
|
+
if model_name not in raw_matrix.index:
|
|
232
|
+
raise ValueError(
|
|
233
|
+
f"Reference model '{model_name}' not found in score matrix. "
|
|
234
|
+
f"Available models: {list(raw_matrix.index)}"
|
|
235
|
+
)
|
|
236
|
+
high = raw_matrix.loc[model_name, dataset]
|
|
237
|
+
low_dict[dataset] = float(low)
|
|
238
|
+
high_dict[dataset] = float(high)
|
|
239
|
+
direction_dict[dataset] = get_direction(metric_lower)
|
|
240
|
+
else:
|
|
241
|
+
direction = get_direction(metric_lower) # raises ValueError for unknowns
|
|
242
|
+
nat_low, nat_high = get_natural_bounds(metric_lower)
|
|
243
|
+
if nat_high is None:
|
|
244
|
+
raise ValueError(
|
|
245
|
+
f"Metric '{metric_name}' on dataset '{dataset}' has no natural "
|
|
246
|
+
f"upper bound: add '{metric_lower}' to metric_type_bounds "
|
|
247
|
+
f"in your config."
|
|
248
|
+
)
|
|
249
|
+
low_dict[dataset] = nat_low
|
|
250
|
+
high_dict[dataset] = nat_high
|
|
251
|
+
direction_dict[dataset] = direction
|
|
252
|
+
|
|
253
|
+
direction_dict.update(metric_direction_override or {})
|
|
254
|
+
return (
|
|
255
|
+
pd.Series(low_dict),
|
|
256
|
+
pd.Series(high_dict),
|
|
257
|
+
direction_dict or None,
|
|
258
|
+
)
|
evaluma/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0)
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
evaluma/benchmark.py
ADDED
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
from functools import cached_property
|
|
2
|
+
|
|
3
|
+
import pandas as pd
|
|
4
|
+
|
|
5
|
+
from evaluma.normalize import normalize
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class Benchmark:
|
|
9
|
+
"""Container for a normalized model-vs-dataset score matrix.
|
|
10
|
+
|
|
11
|
+
After construction the normalized scores are available as ``scores_``.
|
|
12
|
+
Use the analysis methods to compute rankings, comparisons, and profiles.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
raw_matrix: pd.DataFrame,
|
|
18
|
+
*,
|
|
19
|
+
norm_ref_low=None,
|
|
20
|
+
norm_ref_high=None,
|
|
21
|
+
metric_direction=None,
|
|
22
|
+
raw_runs=None,
|
|
23
|
+
):
|
|
24
|
+
"""Initialize and normalize the score matrix.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
raw_matrix: Model × dataset score matrix (models as row index,
|
|
28
|
+
datasets as columns).
|
|
29
|
+
norm_ref_low: Lower normalization reference — scalar, model
|
|
30
|
+
name, or per-dataset dict. ``None`` triggers data-dependent
|
|
31
|
+
bounds and a ``UserWarning``.
|
|
32
|
+
norm_ref_high: Upper normalization reference, same format as
|
|
33
|
+
``norm_ref_low``.
|
|
34
|
+
metric_direction: Dict mapping dataset names to ``"min"`` or
|
|
35
|
+
``"max"``; datasets mapped to ``"min"`` are negated before
|
|
36
|
+
normalization.
|
|
37
|
+
raw_runs: Long-format DataFrame with columns
|
|
38
|
+
``["model", "dataset", "seed", "score"]`` containing
|
|
39
|
+
per-seed scores. When provided, ``iqm_ranking()`` uses
|
|
40
|
+
stratified bootstrap over seeds.
|
|
41
|
+
"""
|
|
42
|
+
self._raw = raw_matrix
|
|
43
|
+
self._norm_ref_low = norm_ref_low
|
|
44
|
+
self._norm_ref_high = norm_ref_high
|
|
45
|
+
self._metric_direction = metric_direction
|
|
46
|
+
self._raw_runs = raw_runs
|
|
47
|
+
|
|
48
|
+
def _normalize(self, matrix):
|
|
49
|
+
import warnings
|
|
50
|
+
|
|
51
|
+
with warnings.catch_warnings():
|
|
52
|
+
if self._norm_ref_low is not None or self._norm_ref_high is not None:
|
|
53
|
+
warnings.simplefilter("ignore", UserWarning)
|
|
54
|
+
return normalize(
|
|
55
|
+
matrix,
|
|
56
|
+
norm_ref_low=self._norm_ref_low,
|
|
57
|
+
norm_ref_high=self._norm_ref_high,
|
|
58
|
+
metric_direction=self._metric_direction,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
@cached_property
|
|
62
|
+
def scores_(self):
|
|
63
|
+
"""Normalized model × dataset score matrix."""
|
|
64
|
+
return self._normalize(self._raw)
|
|
65
|
+
|
|
66
|
+
def _new(self, raw_matrix, raw_runs=None):
|
|
67
|
+
return Benchmark(
|
|
68
|
+
raw_matrix,
|
|
69
|
+
norm_ref_low=self._norm_ref_low,
|
|
70
|
+
norm_ref_high=self._norm_ref_high,
|
|
71
|
+
metric_direction=self._metric_direction,
|
|
72
|
+
raw_runs=raw_runs,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def select_models(self, models):
|
|
76
|
+
"""Subset the benchmark to the given models.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
models: List of model names to retain.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
Benchmark: New benchmark containing only the selected models.
|
|
83
|
+
"""
|
|
84
|
+
raw_runs = None
|
|
85
|
+
if self._raw_runs is not None:
|
|
86
|
+
raw_runs = self._raw_runs[self._raw_runs["model"].isin(models)].reset_index(
|
|
87
|
+
drop=True
|
|
88
|
+
)
|
|
89
|
+
return self._new(self._raw.loc[models], raw_runs=raw_runs)
|
|
90
|
+
|
|
91
|
+
def select_datasets(self, datasets):
|
|
92
|
+
"""Subset the benchmark to the given datasets.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
datasets: List of dataset names to retain.
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Benchmark: New benchmark containing only the selected datasets.
|
|
99
|
+
"""
|
|
100
|
+
raw_runs = None
|
|
101
|
+
if self._raw_runs is not None:
|
|
102
|
+
raw_runs = self._raw_runs[
|
|
103
|
+
self._raw_runs["dataset"].isin(datasets)
|
|
104
|
+
].reset_index(drop=True)
|
|
105
|
+
return self._new(self._raw[datasets], raw_runs=raw_runs)
|
|
106
|
+
|
|
107
|
+
def drop_incomplete(self):
|
|
108
|
+
"""Remove models that have missing scores for any dataset.
|
|
109
|
+
|
|
110
|
+
Returns:
|
|
111
|
+
Benchmark: New benchmark with incomplete models removed.
|
|
112
|
+
"""
|
|
113
|
+
complete = self._raw.index[~self._raw.isna().any(axis=1)]
|
|
114
|
+
raw_runs = None
|
|
115
|
+
if self._raw_runs is not None:
|
|
116
|
+
raw_runs = self._raw_runs[
|
|
117
|
+
self._raw_runs["model"].isin(complete)
|
|
118
|
+
].reset_index(drop=True)
|
|
119
|
+
return self._new(self._raw.loc[complete], raw_runs=raw_runs)
|
|
120
|
+
|
|
121
|
+
def iqm_ranking(self, n_bootstrap=1000, random_state=None):
|
|
122
|
+
"""Compute IQM rankings with stratified bootstrap confidence intervals.
|
|
123
|
+
|
|
124
|
+
Implements the Agarwal et al. 2021 (rliable) IQM on the flat
|
|
125
|
+
run×dataset score array. Requires multiple seeds; use
|
|
126
|
+
``aggregate_ranking()`` for single-run data.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
n_bootstrap: Number of bootstrap samples for the 95 % CI.
|
|
130
|
+
random_state: Seed for the random number generator.
|
|
131
|
+
|
|
132
|
+
Returns:
|
|
133
|
+
IQMResult: Result with ``.table`` and ``.plot()``.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
ValueError: If no seed data is available (``_raw_runs is None``).
|
|
137
|
+
"""
|
|
138
|
+
if self._raw_runs is None:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"iqm_ranking() requires multiple seeds — "
|
|
141
|
+
"use aggregate_ranking() for single-run data."
|
|
142
|
+
)
|
|
143
|
+
from evaluma.methods.iqm import compute_iqm
|
|
144
|
+
from evaluma.normalize import _resolve_bound
|
|
145
|
+
|
|
146
|
+
low = _resolve_bound(self._raw, self._norm_ref_low, use_min=True)
|
|
147
|
+
high = _resolve_bound(self._raw, self._norm_ref_high, use_min=False)
|
|
148
|
+
return compute_iqm(
|
|
149
|
+
self._raw_runs,
|
|
150
|
+
norm_bounds=(low, high, self._metric_direction),
|
|
151
|
+
n_bootstrap=n_bootstrap,
|
|
152
|
+
random_state=random_state,
|
|
153
|
+
)
|
|
154
|
+
|
|
155
|
+
def aggregate_ranking(self, agg="trimmed_mean"):
|
|
156
|
+
"""Compute a point-estimate descriptive ranking (no CI).
|
|
157
|
+
|
|
158
|
+
Works on any benchmark regardless of whether seed data is present.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
agg: Aggregation mode — ``"trimmed_mean"`` (default), ``"mean"``,
|
|
162
|
+
or ``"median"``.
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
AggregateResult: Result with ``.table`` and ``.plot()``.
|
|
166
|
+
|
|
167
|
+
Raises:
|
|
168
|
+
ValueError: If ``agg`` is not a supported mode.
|
|
169
|
+
"""
|
|
170
|
+
from evaluma.methods.aggregate import compute_aggregate
|
|
171
|
+
|
|
172
|
+
return compute_aggregate(self.scores_, agg=agg)
|
|
173
|
+
|
|
174
|
+
def bayesian_comparison(
|
|
175
|
+
self, rope=0.01, reference=None, pairs=None, random_state=None
|
|
176
|
+
):
|
|
177
|
+
"""Compute pairwise Bayesian comparisons via signed-rank test.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
rope: Region of practical equivalence half-width.
|
|
181
|
+
reference: If given, only compare each other model against this
|
|
182
|
+
one.
|
|
183
|
+
pairs: Explicit list of ``(model_a, model_b)`` pairs to test.
|
|
184
|
+
Overrides ``reference``.
|
|
185
|
+
random_state: Seed for baycomp's sampler.
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
BayesianResult: Result with ``.table`` and ``.plot()``.
|
|
189
|
+
"""
|
|
190
|
+
from evaluma.methods.bayesian import compute_bayesian
|
|
191
|
+
|
|
192
|
+
return compute_bayesian(
|
|
193
|
+
self.scores_,
|
|
194
|
+
rope=rope,
|
|
195
|
+
reference=reference,
|
|
196
|
+
pairs=pairs,
|
|
197
|
+
random_state=random_state,
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
def performance_profiles(self):
|
|
201
|
+
"""Compute Dolan-Moré performance profiles.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
ProfileResult: Result with ``.table`` and ``.plot()``.
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
ValueError: If any raw score is zero or negative.
|
|
208
|
+
"""
|
|
209
|
+
from evaluma.methods.profiles import compute_profiles
|
|
210
|
+
|
|
211
|
+
return compute_profiles(self._raw, metric_direction=self._metric_direction)
|