import logging
from collections.abc import Iterable
from typing import TYPE_CHECKING, Any
import pandas as pd
from rics.performance.plot_types import Kind, Unit
from rics.performance.types import ResultsDict
from ._params import CatplotParams
from ._postprocessors import make_postprocessors
if TYPE_CHECKING:
import seaborn
from ..plot_types import Aggregation
[docs]
def plot_run(
run_results: ResultsDict | pd.DataFrame,
*,
x: str | None = None,
hue: str | None = None,
horizontal: bool = False,
unit: Unit | None = None,
kind: Kind = "bar",
names: Iterable[str] = (),
relative_to: str | None = None,
agg: "Aggregation" = "min",
**kwargs: Any,
) -> "seaborn.FacetGrid":
"""Create a :func:`seaborn.catplot` from run results.
.. figure:: /_images/perf_plot_facets.png
Comparison of best-per-group selection functions (from the
:ref:`examples </documentation/examples/notebooks/performance/best-by-group/Best-by-Group.ipynb>`
page).
The `names` argument:
Names may be passed in combination with ``row`` and/or ``col`` arguments to add facets to the
:func:`seaborn.catplot`. If given, the keys in the test data must be of type ``tuple`` with the same length as
`names`. For example, if your test data looks like this:
.. code-block::
test_data = {
("+", 2, 5): +(2**5),
("+", 9, 5): +(9**5),
("+", 10, 5): +(10**5),
("-", 10, 3): -(10**3),
("-", 5, 3): -(5**3),
}
you may pass
.. code-block::
plot_run(
run_results = ...,
names=["sign", "base", "exponent"],
col="sign",
row="exponent",
)
to plot each sign/exponent in a separate facet, comparing only the exponents in the subplots.
Args:
run_results: Output of :meth:`.MultiCaseTimer.run`.
x: X-axis quantity: ``'candidate'``, ``'data'``, or one of `names` (a test-data dimension). If omitted, defaults
to the complement of `hue` (or the higher-cardinality of candidate/data).
hue: Hue quantity: ``'candidate'``, ``'data'``, or one of `names`. If omitted, defaults to the complement of `x`
(or the candidate when `x` is a named dimension).
horizontal: If ``True``, plot the metric on the X-axis instead. The `x` becomes the new Y-axis quantity.
unit: Y-axis time :attr:`~rics.performance.plot_types.Unit`. Not allowed in `relative_to` mode.
kind: The :attr:`~rics.performance.plot_types.Kind` of plot to draw.
names: Test data level names.
relative_to: If given, plot the speedup of each candidate relative to this baseline candidate (see
:func:`.relative_to`) instead of absolute timings. The Y-axis becomes a dimensionless ``speedup`` and a
reference line is drawn at ``1.0``. The baseline itself (always ``1.0``) is represented by the reference
line and omitted from the bars.
agg: How to summarize the repeated timings in `relative_to` mode; one of ``'min'``, ``'median'``, ``'mean'``.
**kwargs: Keyword arguments for :func:`seaborn.catplot` (e.g. ``col``, ``row``, ``col_wrap``).
Returns:
A :class:`seaborn.FacetGrid`.
Raises:
ModuleNotFoundError: If Seaborn isn't installed.
TypeError: For unknown `unit` arguments.
ValueError: For unknown `x`/`hue` arguments, or `unit` combined with `relative_to`.
"""
params = CatplotParams.make(
run_results,
x=x,
hue=hue,
horizontal=horizontal,
unit=unit,
kind=kind,
names=names,
relative_to=relative_to,
agg=agg,
**kwargs,
)
return plot_params(params)
def plot_params(params: CatplotParams) -> "seaborn.axisgrid.FacetGrid":
"""Create a :func:`seaborn.catplot` from :class:`CatplotParams`."""
from seaborn import catplot
kwargs = params.to_kwargs()
postprocessors = make_postprocessors(kwargs, params)
facet_grid = catplot(**kwargs)
logger = logging.getLogger(__package__).getChild("plot")
if logger.isEnabledFor(logging.DEBUG):
from rics.misc import get_public_module
without_data = kwargs.copy()
shape = without_data.pop("data").shape
pretty_func = get_public_module(catplot) + "." + catplot.__qualname__
logger.debug(
f"Calling {pretty_func}(DataFrame[{' x '.join(map(str, shape))}], **kwargs) with:"
f"\n - {params=}"
f"\n - kwargs={without_data}"
f"\n - {postprocessors=}",
extra=without_data,
)
facet_grid.set_titles()
for p in postprocessors:
p(facet_grid)
return facet_grid