Source code for rics.performance._wrapper

import warnings
from collections.abc import Collection, Iterable
from typing import Any

import pandas as pd

from ._multi_case_timer import CandidateMethodArg, MultiCaseTimer, TestDataArg
from ._util import to_dataframe
from .types import DataFunc, DataType, SetupFunc, StratifyArg, Ts


[docs] def run_multivariate_test( candidate_method: CandidateMethodArg[DataType], test_data: TestDataArg[DataType] | DataFunc[*Ts, DataType], # DataFunc[DataFuncP, DataType] *, time_per_candidate: float = 6.0, repeat: int = 5, plot: bool = True, show: bool = True, names: Iterable[str] | None = (), progress: bool = False, setup: SetupFunc[DataType] | None = None, warmup: int = 0, stratify: StratifyArg = None, case_args: Collection[tuple[*Ts]] | None = None, kwargs: Any | None = None, **plot_kwargs: Any, ) -> pd.DataFrame: """Run performance tests for multiple candidate methods on collections of test data. This is a convenience method which combines :meth:`MultiCaseTimer.run() <rics.performance.MultiCaseTimer.run>`, :meth:`~rics.performance.to_dataframe` and -- if plotting is enabled -- :meth:`~rics.performance.plot_run`. For full functionally these methods should be use directly. Args: candidate_method: A dict ``{label: function}``. Alternatively, you may pass a collection of functions or a single function. test_data: A ``{label: data}`` to evaluate candidates on. You may also pass a list of data, which will be converted to a dict as above. Data may also be generated by passing a callable. time_per_candidate: Minimum runtime per repetition and candidate label. When `stratify` is set this budget applies **per** ``(candidate, stratum)`` **instead**, so total runtime scales with the number of strata. repeat: Number of times to repeat for all candidates per data label. plot: If ``True``, plot a figure using :meth:`~rics.performance.plot_run`. show: If ``True``, attempt to display the figure. Ignored when ``plot=False``. names: Level names for tuple keys in the data (creates new columns). See :func:`.plot_run` for details. Set to ``None`` to disable derived names when `test_data` is callable. progress: If ``True``, display progress. Uses ``tqdm`` on a TTY and falls back to periodic logging otherwise (so ``tqdm`` is optional). setup: A callable ``(data) -> data`` invoked -- **not** measured -- before each timed repetition to produce a fresh input (mirrors :py:class:`timeit.Timer`'s ``setup``). Use for candidates that mutate their input, or to reset shared state (e.g. caches) between repetitions. warmup: Number of untimed calls per candidate/data pair before timing begins (warms caches/JIT/imports). stratify: Groups variants of comparable cost so that the iteration count is calibrated once per ``(candidate, stratum)`` instead of once per candidate function. Using ``"auto"`` implicitly calls :meth:`~rics.performance.MultiCaseTimer.fit_strata` the first time. Set to ``None`` to disable. case_args: Collection of positional arguments for a `test_data` callable. kwargs: Shared keyword arguments for a `test_data` callable. **plot_kwargs: See :func:`.plot_run` for details. Ignored if ``plot=False``. Returns: A long-form DataFrame of results. Raises: RuntimeError: If Seaborn isn't installed and ``plot=True``. TypeError: If `case_args` or `kwargs` are set when `test_data` is not a callable. ValueError: If `case_args` is empty and `test_data` is a callable. See Also: The :func:`~rics.performance.plot_run` and :func:`~rics.performance.get_best` functions. """ if plot: _verify_can_plot() elif plot_kwargs: # TODO(7.0.0): Raise. warnings.warn( f"Ignoring {plot_kwargs=} since {plot=}; this will raise in a future version.", FutureWarning, stacklevel=2, ) timer: MultiCaseTimer[DataType, *Ts] = MultiCaseTimer( candidate_method, test_data, case_args=case_args, kwargs=kwargs, setup=setup, warmup=warmup, ) run_results = timer.run( time_per_candidate=time_per_candidate, repeat=repeat, progress=progress, stratify=stratify, ) if names is None: names = () else: names = [*names] if not names and timer.is_data_generated: names = timer.derive_names() data = to_dataframe(run_results, names=names) if plot: from matplotlib.pyplot import show as show_figure from ._plot import plot_run plot_run(data, names=names, **plot_kwargs) if show: show_figure(block=False) return data
def _verify_can_plot() -> None: from importlib.util import find_spec if find_spec("seaborn") is None: msg = ( "Package `seaborn` not installed. Run of one:" "\n pip install seaborn" "\n pip install rics[plotting]" "\nto enable plots." ) raise RuntimeError(msg)