Skip to content

Utilities

evalica.BootstrapResult dataclass

The result of a bootstrap operation.

Attributes:

Name Type Description
result Result

The original point estimates (from the full dataset).

low Series[float]

Lower bounds of the confidence interval.

high Series[float]

Upper bounds of the confidence interval.

stderr Series[float]

Standard errors of the scores.

distribution DataFrame

The full bootstrap distribution (resamples x elements).

index Index

The index of elements.

Source code in evalica/__init__.py
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
@dataclass(frozen=True)
class BootstrapResult:
    """
    The result of a bootstrap operation.

    Attributes:
        result: The original point estimates (from the full dataset).
        low: Lower bounds of the confidence interval.
        high: Upper bounds of the confidence interval.
        stderr: Standard errors of the scores.
        distribution: The full bootstrap distribution (resamples x elements).
        index: The index of elements.

    """

    result: Result
    low: pd.Series[float]
    high: pd.Series[float]
    stderr: pd.Series[float]
    distribution: pd.DataFrame = field(repr=False)
    index: pd.Index

evalica.Winner

Bases: IntEnum

The outcome of the pairwise comparison.

Source code in evalica/__init__.py
22
23
24
25
26
27
28
29
30
31
32
class Winner(IntEnum):
    """The outcome of the pairwise comparison."""

    Draw = 0
    """There is a tie."""

    X = 1
    """The first element won."""

    Y = 2
    """The second element won."""

Draw = 0 class-attribute instance-attribute

There is a tie.

X = 1 class-attribute instance-attribute

The first element won.

Y = 2 class-attribute instance-attribute

The second element won.

evalica.WINNERS = list(Winner) module-attribute

Known values of Winner.

evalica.PYO3_AVAILABLE = True module-attribute

The Rust extension is available and can be used for performance-critical operations.

Please set the environment variable EVALICA_NIJE_BRZO to disable it.

evalica.SOLVER = 'pyo3' if PYO3_AVAILABLE else 'naive' module-attribute

The default solver.

evalica.bootstrap(method, xs, ys, winners, weights=None, index=None, win_weight=1.0, tie_weight=0.5, solver=SOLVER, *, n_resamples=1000, confidence_level=0.95, bootstrap_method='BCa', random_state=None, **kwargs)

Compute weighted bootstrap confidence intervals for the given pairwise comparison.

Parameters:

Name Type Description Default
xs Collection[T_contra]

The left-hand side elements.

required
ys Collection[T_contra]

The right-hand side elements.

required
winners Collection[Winner]

The winner elements.

required
weights Collection[float] | None

The example weights.

None
method RankingMethod[T_contra]

The ranking method to use.

required
index Index | None

The index.

None
win_weight float

The win weight.

1.0
tie_weight float

The tie weight.

0.5
solver Literal['naive', 'pyo3']

The solver.

SOLVER
n_resamples int

The number of resamples.

1000
confidence_level float

The confidence level.

0.95
bootstrap_method Literal['percentile', 'basic', 'BCa']

The bootstrap method (percentile, basic, or BCa).

'BCa'
random_state int | Generator | None

The random state.

None
**kwargs Any

The additional arguments for the ranking method.

{}

Returns:

Type Description
BootstrapResult

The bootstrap result.

Source code in evalica/__init__.py
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
def bootstrap(
    method: RankingMethod[T_contra],
    xs: Collection[T_contra],
    ys: Collection[T_contra],
    winners: Collection[Winner],
    weights: Collection[float] | None = None,
    index: pd.Index | None = None,
    win_weight: float = 1.0,
    tie_weight: float = 0.5,
    solver: Literal["naive", "pyo3"] = SOLVER,
    *,
    n_resamples: int = 1000,
    confidence_level: float = 0.95,
    bootstrap_method: Literal["percentile", "basic", "BCa"] = "BCa",
    random_state: int | np.random.Generator | None = None,
    **kwargs: Any,  # noqa: ANN401
) -> BootstrapResult:
    """
    Compute weighted bootstrap confidence intervals for the given pairwise comparison.

    Args:
        xs: The left-hand side elements.
        ys: The right-hand side elements.
        winners: The winner elements.
        weights: The example weights.
        method: The ranking method to use.
        index: The index.
        win_weight: The win weight.
        tie_weight: The tie weight.
        solver: The solver.
        n_resamples: The number of resamples.
        confidence_level: The confidence level.
        bootstrap_method: The bootstrap method (percentile, basic, or BCa).
        random_state: The random state.
        **kwargs: The additional arguments for the ranking method.

    Returns:
        The bootstrap result.

    """
    _, _, index = indexing(xs, ys, index)

    result = method(
        xs=xs,
        ys=ys,
        winners=winners,
        weights=weights,
        index=index,
        win_weight=win_weight,
        tie_weight=tie_weight,
        solver=solver,
        **kwargs,
    )

    weights_array = np.array(_wrap_weights(weights, len(xs)), dtype=np.float64)

    samples = (np.array(xs, dtype=object), np.array(ys, dtype=object), np.array(winners, dtype=np.uint8), weights_array)

    def statistic(*data: tuple[Any, ...]) -> npt.NDArray[np.float64]:
        xs_sample, ys_sample, winners_sample, weights_sample = data

        result_sample = method(
            xs=xs_sample,
            ys=ys_sample,
            winners=list(winners_sample),  # TODO: ensure no copying needed
            index=index,
            weights=weights_sample,
            win_weight=win_weight,
            tie_weight=tie_weight,
            solver=solver,
            **kwargs,
        )

        return cast("npt.NDArray[np.float64]", result_sample.scores.to_numpy(dtype=np.float64))

    bootstrap_result = scipy_bootstrap(
        data=samples,
        statistic=statistic,
        paired=True,
        n_resamples=n_resamples,
        confidence_level=confidence_level,
        method=bootstrap_method,
        random_state=random_state,
        vectorized=False,
    )

    return BootstrapResult(
        result=result,
        low=pd.Series(bootstrap_result.confidence_interval.low, index=index, name="low"),
        high=pd.Series(bootstrap_result.confidence_interval.high, index=index, name="high"),
        stderr=pd.Series(bootstrap_result.standard_error, index=index, name="stderr"),
        distribution=pd.DataFrame(bootstrap_result.bootstrap_distribution.T, columns=index),
        index=index,
    )

evalica.indexing(xs, ys, index=None)

Map the input elements into their numerical representations.

Parameters:

Name Type Description Default
xs Collection[T_contra]

The left-hand side elements.

required
ys Collection[T_contra]

The right-hand side elements.

required
index Index | None

The index; if provided, all elements in xs and ys must be present in it.

None

Returns:

Type Description
tuple[list[int], list[int], Index]

The tuple containing the numerical representations of the input elements and the corresponding index.

Source code in evalica/__init__.py
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
def indexing(
    xs: Collection[T_contra],
    ys: Collection[T_contra],
    index: pd.Index | None = None,
) -> tuple[list[int], list[int], pd.Index]:
    """
    Map the input elements into their numerical representations.

    Args:
        xs: The left-hand side elements.
        ys: The right-hand side elements.
        index: The index; if provided, all elements in xs and ys must be present in it.

    Returns:
        The tuple containing the numerical representations of the input elements and the corresponding index.

    """
    if index is None:
        labels = list(dict.fromkeys([*xs, *ys]))
        index = pd.Index(labels)

    xi = index.get_indexer(cast("pd.Index", xs))
    yi = index.get_indexer(cast("pd.Index", ys))

    if (xi < 0).any() or (yi < 0).any():
        msg = "Unknown element in reindexing"
        raise TypeError(msg)

    return xi.tolist(), yi.tolist(), index

evalica.matrices(xs_indexed, ys_indexed, winners, index, weights=None, solver=SOLVER)

Build win and tie matrices from the given elements.

Parameters:

Name Type Description Default
xs_indexed Collection[int]

The left-hand side elements.

required
ys_indexed Collection[int]

The right-hand side elements.

required
winners Collection[Winner]

The winner elements.

required
index Index

The index.

required
weights Collection[float] | None

The example weights.

None
solver Literal['naive', 'pyo3']

The solver.

SOLVER

Returns:

Type Description
MatricesResult

The win and tie matrices.

Source code in evalica/__init__.py
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
def matrices(
    xs_indexed: Collection[int],
    ys_indexed: Collection[int],
    winners: Collection[Winner],
    index: pd.Index,
    weights: Collection[float] | None = None,
    solver: Literal["naive", "pyo3"] = SOLVER,
) -> MatricesResult:
    """
    Build win and tie matrices from the given elements.

    Args:
        xs_indexed: The left-hand side elements.
        ys_indexed: The right-hand side elements.
        winners: The winner elements.
        index: The index.
        weights: The example weights.
        solver: The solver.

    Returns:
        The win and tie matrices.

    """
    weights = _wrap_weights(weights, len(xs_indexed))

    if solver == "pyo3":
        if not PYO3_AVAILABLE:
            raise SolverError(solver)

        win_matrix, tie_matrix = _brzo.matrices(
            xs=xs_indexed,
            ys=ys_indexed,
            winners=winners,
            weights=weights,
            total=len(index),
        )
    else:
        win_matrix, tie_matrix = matrices_naive(
            xs=xs_indexed,
            ys=ys_indexed,
            winners=winners,
            weights=weights,
            total=len(index),
        )

    return MatricesResult(
        win_matrix=win_matrix,
        tie_matrix=tie_matrix,
        index=index,
    )

evalica.MatricesResult dataclass

The win and tie matrices.

Attributes:

Name Type Description
win_matrix NDArray[float64]

The matrix representing wins between the elements.

tie_matrix NDArray[float64]

The matrix representing ties between the elements; it is always symmetric.

index Index

The index.

Source code in evalica/__init__.py
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
@dataclass(frozen=True)
class MatricesResult:
    """
    The win and tie matrices.

    Attributes:
        win_matrix: The matrix representing wins between the elements.
        tie_matrix: The matrix representing ties between the elements; it is always symmetric.
        index: The index.

    """

    win_matrix: npt.NDArray[np.float64]
    tie_matrix: npt.NDArray[np.float64]
    index: pd.Index

evalica.RankingMethod

Bases: Protocol[T_contra]

The ranking method protocol.

Source code in evalica/__init__.py
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
@runtime_checkable
class RankingMethod(Protocol[T_contra]):
    """The ranking method protocol."""

    def __call__(
        self,
        xs: Collection[T_contra],
        ys: Collection[T_contra],
        winners: Collection[Winner],
        index: pd.Index | None = None,
        weights: Collection[float] | None = None,
        **kwargs: Any,  # noqa: ANN401
    ) -> Result:
        """
        Compute the scores for the given pairwise comparison.

        Args:
            xs: The left-hand side elements.
            ys: The right-hand side elements.
            winners: The winner elements.
            index: The index.
            weights: The example weights.
            **kwargs: The additional keyword arguments.

        Returns:
            The ranking result.

        """
        ...

__call__(xs, ys, winners, index=None, weights=None, **kwargs)

Compute the scores for the given pairwise comparison.

Parameters:

Name Type Description Default
xs Collection[T_contra]

The left-hand side elements.

required
ys Collection[T_contra]

The right-hand side elements.

required
winners Collection[Winner]

The winner elements.

required
index Index | None

The index.

None
weights Collection[float] | None

The example weights.

None
**kwargs Any

The additional keyword arguments.

{}

Returns:

Type Description
Result

The ranking result.

Source code in evalica/__init__.py
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
def __call__(
    self,
    xs: Collection[T_contra],
    ys: Collection[T_contra],
    winners: Collection[Winner],
    index: pd.Index | None = None,
    weights: Collection[float] | None = None,
    **kwargs: Any,  # noqa: ANN401
) -> Result:
    """
    Compute the scores for the given pairwise comparison.

    Args:
        xs: The left-hand side elements.
        ys: The right-hand side elements.
        winners: The winner elements.
        index: The index.
        weights: The example weights.
        **kwargs: The additional keyword arguments.

    Returns:
        The ranking result.

    """
    ...

evalica.Result

Bases: Protocol

The result protocol.

Attributes:

Name Type Description
scores Series[float]

The element scores.

index Index

The index.

Source code in evalica/__init__.py
299
300
301
302
303
304
305
306
307
308
309
310
311
@runtime_checkable
class Result(Protocol):
    """
    The result protocol.

    Attributes:
        scores: The element scores.
        index: The index.

    """

    scores: pd.Series[float]
    index: pd.Index

evalica.pairwise_frame(scores)

Create a data frame out of the estimated pairwise scores.

Parameters:

Name Type Description Default
scores Series[float]

The element scores.

required

Returns:

Type Description
DataFrame

The data frame representing pairwise scores between the elements.

Source code in evalica/__init__.py
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
def pairwise_frame(scores: pd.Series[float]) -> pd.DataFrame:
    """
    Create a data frame out of the estimated pairwise scores.

    Args:
        scores: The element scores.

    Returns:
        The data frame representing pairwise scores between the elements.

    """
    arr = np.asarray(scores.array, dtype=np.float64)
    return pd.DataFrame(pairwise_scores(arr), index=scores.index, columns=scores.index)

evalica.pairwise_scores(scores, solver=SOLVER)

Estimate the pairwise scores.

Parameters:

Name Type Description Default
scores NDArray[float64]

The element scores.

required
solver Literal['naive', 'pyo3']

The solver.

SOLVER

Returns:

Type Description
NDArray[float64]

The matrix representing pairwise scores between the elements.

Source code in evalica/__init__.py
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
def pairwise_scores(
    scores: npt.NDArray[np.float64],
    solver: Literal["naive", "pyo3"] = SOLVER,
) -> npt.NDArray[np.float64]:
    """
    Estimate the pairwise scores.

    Args:
        scores: The element scores.
        solver: The solver.

    Returns:
        The matrix representing pairwise scores between the elements.

    """
    if scores.ndim != 1:
        raise ScoreDimensionError(scores.ndim)

    if solver == "pyo3":
        if not PYO3_AVAILABLE:
            raise SolverError(solver)

        return _brzo.pairwise_scores(scores)

    return pairwise_scores_naive(scores)

evalica.__version__ = '0.4.0' module-attribute

The version of Evalica.