Skip to content

Plot Base Classes

Custom plots subclass BasePlot and implement run(self, benchmark_results). The chart helpers below build on BasePlot and add a create(...) method for common chart types, so most plots subclass one of them rather than BasePlot directly.

BasePlot

Bases: RegisterableComponent, ABC

Base interface for all plot components.

Subclasses should implement the run method.

run(benchmark_results: BenchmarkResultContainer, save_dir: str | None = None) -> None abstractmethod

Generate plot output from benchmark results.

Parameters:

SeabornPlot

Bases: BasePlot, ABC

Base of a seaborn-oriented plot with a shared figure and axis configuration.

Every attribute below can be set when the plot is constructed, e.g. RuntimePlot(figure=Figure(width=12, file_formats=("pgf", "png"))). The flat spelling those options used to have - width=12 - is still accepted.

Attributes:

  • figure (Figure) –

    The figure this plot is drawn on and the files written from it - its size, resolution, output formats, and whether it is opened in a window.

  • theme (Theme | None) –

    The seaborn theme the figure is drawn under and the gridlines behind the marks. By default seaborn's whitegrid with lines along the value axis; None for matplotlib's own look, without a grid.

  • missing (Missing) –

    What becomes of the values the plot cannot draw - an infinite time to solution, a metric that failed - and how the figure says they were there.

Requires

Install the 'pre-defined' extra: pip install luna-bench[pre-defined]

figure: Figure = Figure() class-attribute instance-attribute

The figure this plot is drawn on and the files written from it.

theme: Theme | None = Theme() class-attribute instance-attribute

The seaborn theme the figure is drawn under, and the gridlines behind the marks.

A figure is read against its axis, so it is drawn with the lines that make that possible unless asked otherwise. None takes the theme and the grid away.

missing: Missing = Missing() class-attribute instance-attribute

What becomes of the values the plot cannot draw, and how it says they were there.

option_bundles: dict[str, type[OptionBundle]] = {'style': PlotStyle} class-attribute

Constructor arguments that configure several fields at once, and the bundle each takes.

A style is spread over the bundle fields rather than stored, so a benchmark can hand the same look to every plot while each keeps what it says itself. Applied least specific first: the shared style, then a bundle passed to the plot, then a flat option.

draw_into(axes: Axes, benchmark_results: BenchmarkResultContainer) -> None

Run this plot onto an existing axes rather than into a figure of its own.

Everything the plot draws goes through the pyplot state, so making that axes the current one is enough to redirect it. The figure, the files, and the window stay the caller's business - which is what lets several plots share one figure, e.g. the summary grid.

Parameters:

  • axes (Axes) –

    The axes to draw on.

  • benchmark_results (BenchmarkResultContainer) –

    Aggregated benchmark data handed to :meth:run.

resolve_missing(df: pd.DataFrame, column: str, *, by: str | None = None, within: str | None = None) -> tuple[pd.DataFrame, dict[tuple[str, str], int]]

Return the drawable rows and how many of them were not, per category.

A metric with nothing to report says so with a None or an infinity - a time to solution of a run that never reached the optimum is the usual one, since the expected time to something that did not happen is unbounded. Neither is a height a bar can have, and leaving them in poisons the aggregate: one infinity turns the mean of an algorithm into an infinity, and a missing value silently shortens it.

What happens to them is :attr:missing, and by default it is nothing: the plot raises rather than quietly showing a mean over fewer models than it claims. Asked to carry on, it either leaves them out or fills them from the values that could be drawn - Missing(policy="max") puts them just past the tallest bar, which is where "worse than everything here" belongs. Either way they are counted and a warning is logged: a bar resting on half its models is a different statement from one resting on all of them, and that is not visible in the bar itself.

Parameters:

  • df (DataFrame) –

    The plotting data.

  • column (str) –

    Column holding the plotted value.

  • by (str | None, default: None ) –

    Column whose categories the missing values are counted per, e.g. the x-axis of a bar plot. Without one they are counted under "".

  • within (str | None, default: None ) –

    Column that splits those categories further, e.g. the grouping of a bar plot. Counting per group is what lets a figure mark the one bar of a group that lost values rather than the whole category it sits in.

Returns:

  • tuple[DataFrame, dict[tuple[str, str], int]] –

    The rows to draw, and the number of missing values per category and group - the group is "" where there is none. The mapping is empty when nothing was missing.

Raises:

  • PlotMissingValuesError –

    If values are missing and the policy is "raise".

place_legend(axes: Axes, handles: list[Any] | None = None, labels: list[str] | None = None) -> None

Put the legend beside the axes, whatever drew it.

Outside the axes for a figure of its own: a legend inside sits on top of the data, and which corner is free depends on the run rather than on the plot - the figure would move its own key around as the numbers change. Beside it, the key is always in the same place and covers nothing.

A panel of someone else's figure is the exception. The room beside it belongs to the panel next to it, so a key anchored there is drawn over a neighbour rather than over the data; inside the panel it stays within the space the plot was given.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

  • handles (list[Any] | None, default: None ) –

    Legend handles, by default the ones already on the axes.

  • labels (list[str] | None, default: None ) –

    Their labels, by default the ones already on the axes.

note_missing(handles: list[Any], labels: list[str], missing: dict[tuple[str, str], int]) -> None

Add the legend entry that says how many values the figure could not draw.

What a plot can say beyond that depends on what it draws. A bar has a slot of its own to put a cross under, so BarPlot marks the categories themselves; a point in a cloud or a step of a sweep has no slot, and the count in the key is the whole statement there - enough that a filled value is not read as a measured one.

Parameters:

  • handles (list[Any]) –

    Legend handles, extended in place.

  • labels (list[str]) –

    Their labels, extended in place alongside handles.

  • missing (dict[tuple[str, str], int]) –

    Number of missing values per category and group, as counted by :meth:resolve_missing.

apply_theme() -> None

Install the seaborn theme this plot is drawn under, unless it has none.

The theme is matplotlib's global state rather than a property of one figure, so it is installed before the figure is built and left in place afterwards: a benchmark themes its plots by handing every one of them the same Theme, not by each plot putting the previous look back.

apply_grid(axes: Axes) -> None

Draw the gridlines the theme asks for, behind everything else on axes.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

setup_figure() -> None

Create a matplotlib figure, unless the plot is drawing into a shared axes.

save_figure(save_dir: str) -> list[Path]

Write the current figure to save_dir once per configured file format.

Parameters:

  • save_dir (str) –

    Directory to save the figure into. Created if it does not exist.

Returns:

  • list[Path] –

    Paths that were written successfully.

finalize_plot(xlabel: str, ylabel: str, title: str, ylim: tuple[float, float] | None = None, x_rotation: int = 45, save_dir: str | None = None) -> None

Apply common axis labels, title, limits, and display behavior.

Parameters:

  • xlabel (str) –

    Label for the x-axis.

  • ylabel (str) –

    Label for the y-axis.

  • title (str) –

    Plot title.

  • ylim (tuple[float, float] | None, default: None ) –

    Lower and upper y-axis limits, by default None.

  • x_rotation (int, default: 45 ) –

    Rotation angle for x-axis tick labels, by default 45.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.

run(benchmark_results: BenchmarkResultContainer, save_dir: str | None = None) -> None abstractmethod

Generate plot output from benchmark results.

Parameters:

BarPlot

Bases: SeabornPlot, ABC

Base helper for generating aggregated seaborn bar plots.

Subclasses turn benchmark results into row dictionaries and hand them to :meth:create; everything below is shared configuration a user can set on any of them at construction time, grouped into bundles by what it configures, e.g. RuntimePlot(annotation=Annotation(enabled=False)). The flat spelling those options used to have - annotate=False - is still accepted.

Attributes:

  • x (Dimension) –

    What the bars are - one per algorithm by default. Also titles the axis.

  • y (MetricDimension) –

    What the bars measure - the attribute read off each result, and its axis title.

  • aggregation (Aggregation) –

    Aggregation applied per x category, by default the mean over the models.

  • errorbars (ErrorBars) –

    The error bars drawn on top of the bars: what they show, their colour, and the caps that turn them into a T.

  • annotation (Annotation | None) –

    The values written above the bars, by default None - none are written.

  • grouping (Dimension | None) –

    What splits each bar into a group of bars: ModelDimension, AlgorithmDimension, FeatureDimension or ParameterDimension.

Examples:

Split the bars by a per-model category and write the figure for LaTeX:

>>> plot = RuntimePlot(
...     grouping=FeatureDimension(feature=UseCaseFeature, label="Use case"),
...     figure=Figure(file_formats=("pgf", "png")),
... )
>>> bench.add_plot(name="runtime", plot=plot)
Requires

Install the 'pre-defined' extra: pip install luna-bench[pre-defined]

See Also

SeabornPlot : Figure size, output formats, and saving.

x: Dimension = AlgorithmDimension() class-attribute instance-attribute

What the bars are: one per value of this dimension, and its title on the axis.

y: MetricDimension = MetricDimension('value') class-attribute instance-attribute

What the bars measure: the attribute read off each result, and its title on the axis.

aggregation: Aggregation = Aggregation.MEAN class-attribute instance-attribute

Aggregation applied to the values of an x category, by default their mean.

errorbars: ErrorBars | None = ErrorBars() class-attribute instance-attribute

The error bars drawn on top of the bars: what they show, their colour and caps.

None draws none, the same as ErrorBars(spec=None).

annotation: Annotation | None = None class-attribute instance-attribute

The values written above the bars - how they are formatted and how large.

None, the default, writes none: a bar chart is read off its axis, and a number above every bar is worth its clutter only when the exact value is the point. Pass an Annotation to turn them on, empty for the defaults.

grouping: Dimension | None = None class-attribute instance-attribute

What splits each bar into a group of bars.

One of the groupers - ModelDimension, AlgorithmDimension, FeatureDimension, ParameterDimension - or None, which leaves the bars ungrouped.

figure: Figure = Figure() class-attribute instance-attribute

The figure this plot is drawn on and the files written from it.

theme: Theme | None = Theme() class-attribute instance-attribute

The seaborn theme the figure is drawn under, and the gridlines behind the marks.

A figure is read against its axis, so it is drawn with the lines that make that possible unless asked otherwise. None takes the theme and the grid away.

missing: Missing = Missing() class-attribute instance-attribute

What becomes of the values the plot cannot draw, and how it says they were there.

option_bundles: dict[str, type[OptionBundle]] = {'style': PlotStyle} class-attribute

Constructor arguments that configure several fields at once, and the bundle each takes.

A style is spread over the bundle fields rather than stored, so a benchmark can hand the same look to every plot while each keeps what it says itself. Applied least specific first: the shared style, then a bundle passed to the plot, then a flat option.

apply_grouping(benchmark_results: BenchmarkResultContainer, rows: list[dict[str, Any]]) -> dict[str, Any]

Split rows into groups along :attr:grouping.

What that means is the grouper's business - a column of the plotted data, a value looked up per model, or a setting the algorithms were configured with - and so is deciding that it does not apply, in which case the bars stay ungrouped.

Parameters:

  • benchmark_results (BenchmarkResultContainer) –

    Benchmark data the feature results and algorithm configurations are read from.

  • rows (list[dict[str, Any]]) –

    Row-oriented plot data, annotated - and, where a grouping applies to only part of the data, reduced - in place.

Returns:

  • dict[str, Any] –

    Keyword arguments to forward to :meth:create. Empty when no grouping applies, so call sites can splat it unconditionally.

draw(*, benchmark_results: BenchmarkResultContainer, rows: list[dict[str, Any]], save_dir: str | None = None, **overrides: Any) -> None

Group rows and draw them with the display configuration of this plot.

This is what turns the declared fields - :attr:x, :attr:title, :attr:hline and the rest - into a :meth:create call, so a subclass only has to say which rows it plots. Doing it in one place is also what keeps :attr:group_by working for every bar plot rather than for those that remember to apply it.

Parameters:

  • benchmark_results (BenchmarkResultContainer) –

    Benchmark data, used to look up the groups of a feature :attr:group_by.

  • rows (list[dict[str, Any]]) –

    Row-oriented plot data.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.

  • **overrides (Any, default: {} ) –

    Keyword arguments forwarded to :meth:create, overriding the fields.

transform_rows(rows: list[dict[str, Any]], x: str | None, group: str | None) -> list[dict[str, Any]]

Return the rows to plot, by default the rows as they are.

A subclass that has to reduce its rows before they are drawn - pooling counts into a single ratio, say - overrides this rather than :meth:run, so it keeps the shared grouping and display handling. It is told what the bars and the groups turned out to be, since that is what a row has to keep to stay one of them.

Parameters:

  • rows (list[dict[str, Any]]) –

    Row-oriented plot data, already annotated with the dimensions' columns.

  • x (str | None) –

    Column the bars are drawn per, or None when the plot has no rows.

  • group (str | None) –

    Column the bars are split by, or None when they are ungrouped.

Returns:

create(*, rows: list[dict[str, Any]], xlabel: str, ylabel: str, title: str, x: str = 'x', y: str = 'y', aggregation: Aggregation = Aggregation.MEAN, errorbar: ErrorBar | str = AUTO_ERRORBAR, hue: str | None = None, hline: float | None = None, hline_label: str | None = None, hcolor: str = REFERENCE_LINE_COLOUR, baseline: float | None = None, ylim: tuple[float, float] | None = None, legend: bool = False, save_dir: str | None = None, **kwargs: Any) -> None

Create a bar plot from row-oriented data.

Parameters:

  • rows (dict[str, Any]) –

    Row-oriented mapping used to construct the plotting DataFrame.

  • xlabel (str) –

    Label for the x-axis.

  • ylabel (str) –

    Label for the y-axis.

  • title (str) –

    Plot title.

  • x (str, default: 'x' ) –

    Column name mapped to the x-axis, by default "x".

  • y (str, default: 'y' ) –

    Column name mapped to the y-axis, by default "y".

  • aggregation (Aggregation, default: MEAN ) –

    Aggregation strategy applied by seaborn, by default Aggregation.MEAN.

  • errorbar (ErrorBar | str, default: AUTO_ERRORBAR ) –

    Seaborn error bar specification ("sd", ("ci", 95), None to disable). By default "auto", which takes the error bar from aggregation: the spread of the samples for means, none for min/max.

  • hue (str | None, default: None ) –

    Optional grouping column for grouped bars, by default None.

  • hline (float | None, default: None ) –

    Optional horizontal reference line value, by default None.

  • hline_label (str | None, default: None ) –

    Legend label for the horizontal reference line, by default None.

  • hcolor (str, default: REFERENCE_LINE_COLOUR ) –

    Colour of the horizontal reference line, by default black.

  • baseline (float | None, default: None ) –

    Height of a solid black baseline marking where the bars start, by default None. Unlike hline it carries no label and stays out of the legend - it says where zero is, it does not name a target.

  • ylim (tuple[float, float] | None, default: None ) –

    Lower and upper y-axis limits, by default None.

  • legend (bool, default: False ) –

    Whether seaborn should create a legend for hue groups, by default False.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments forwarded to :func:seaborn.barplot. They override the defaults computed here, so anything seaborn understands (palette, saturation, capsize, err_kws, ...) can be tuned from the call site.

annotation_text(value: float) -> str

Return the text written above a bar of value.

Applies :attr:annotate_format, unless :attr:annotate_max_decimals allows the value to be written as a plain decimal instead of in scientific notation. A value read off a percent axis is written as a percentage, so the annotation says the same thing as the axis it stands on - unless a format was asked for, which wins.

Parameters:

  • value (float) –

    The aggregated value of one bar.

Returns:

  • str –

    The annotation, e.g. "0.000057" rather than "5.67e-05".

run(benchmark_results: BenchmarkResultContainer, save_dir: str | None = None) -> None abstractmethod

Generate plot output from benchmark results.

Parameters:

draw_into(axes: Axes, benchmark_results: BenchmarkResultContainer) -> None

Run this plot onto an existing axes rather than into a figure of its own.

Everything the plot draws goes through the pyplot state, so making that axes the current one is enough to redirect it. The figure, the files, and the window stay the caller's business - which is what lets several plots share one figure, e.g. the summary grid.

Parameters:

  • axes (Axes) –

    The axes to draw on.

  • benchmark_results (BenchmarkResultContainer) –

    Aggregated benchmark data handed to :meth:run.

resolve_missing(df: pd.DataFrame, column: str, *, by: str | None = None, within: str | None = None) -> tuple[pd.DataFrame, dict[tuple[str, str], int]]

Return the drawable rows and how many of them were not, per category.

A metric with nothing to report says so with a None or an infinity - a time to solution of a run that never reached the optimum is the usual one, since the expected time to something that did not happen is unbounded. Neither is a height a bar can have, and leaving them in poisons the aggregate: one infinity turns the mean of an algorithm into an infinity, and a missing value silently shortens it.

What happens to them is :attr:missing, and by default it is nothing: the plot raises rather than quietly showing a mean over fewer models than it claims. Asked to carry on, it either leaves them out or fills them from the values that could be drawn - Missing(policy="max") puts them just past the tallest bar, which is where "worse than everything here" belongs. Either way they are counted and a warning is logged: a bar resting on half its models is a different statement from one resting on all of them, and that is not visible in the bar itself.

Parameters:

  • df (DataFrame) –

    The plotting data.

  • column (str) –

    Column holding the plotted value.

  • by (str | None, default: None ) –

    Column whose categories the missing values are counted per, e.g. the x-axis of a bar plot. Without one they are counted under "".

  • within (str | None, default: None ) –

    Column that splits those categories further, e.g. the grouping of a bar plot. Counting per group is what lets a figure mark the one bar of a group that lost values rather than the whole category it sits in.

Returns:

  • tuple[DataFrame, dict[tuple[str, str], int]] –

    The rows to draw, and the number of missing values per category and group - the group is "" where there is none. The mapping is empty when nothing was missing.

Raises:

  • PlotMissingValuesError –

    If values are missing and the policy is "raise".

place_legend(axes: Axes, handles: list[Any] | None = None, labels: list[str] | None = None) -> None

Put the legend beside the axes, whatever drew it.

Outside the axes for a figure of its own: a legend inside sits on top of the data, and which corner is free depends on the run rather than on the plot - the figure would move its own key around as the numbers change. Beside it, the key is always in the same place and covers nothing.

A panel of someone else's figure is the exception. The room beside it belongs to the panel next to it, so a key anchored there is drawn over a neighbour rather than over the data; inside the panel it stays within the space the plot was given.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

  • handles (list[Any] | None, default: None ) –

    Legend handles, by default the ones already on the axes.

  • labels (list[str] | None, default: None ) –

    Their labels, by default the ones already on the axes.

note_missing(handles: list[Any], labels: list[str], missing: dict[tuple[str, str], int]) -> None

Add the legend entry that says how many values the figure could not draw.

What a plot can say beyond that depends on what it draws. A bar has a slot of its own to put a cross under, so BarPlot marks the categories themselves; a point in a cloud or a step of a sweep has no slot, and the count in the key is the whole statement there - enough that a filled value is not read as a measured one.

Parameters:

  • handles (list[Any]) –

    Legend handles, extended in place.

  • labels (list[str]) –

    Their labels, extended in place alongside handles.

  • missing (dict[tuple[str, str], int]) –

    Number of missing values per category and group, as counted by :meth:resolve_missing.

apply_theme() -> None

Install the seaborn theme this plot is drawn under, unless it has none.

The theme is matplotlib's global state rather than a property of one figure, so it is installed before the figure is built and left in place afterwards: a benchmark themes its plots by handing every one of them the same Theme, not by each plot putting the previous look back.

apply_grid(axes: Axes) -> None

Draw the gridlines the theme asks for, behind everything else on axes.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

setup_figure() -> None

Create a matplotlib figure, unless the plot is drawing into a shared axes.

save_figure(save_dir: str) -> list[Path]

Write the current figure to save_dir once per configured file format.

Parameters:

  • save_dir (str) –

    Directory to save the figure into. Created if it does not exist.

Returns:

  • list[Path] –

    Paths that were written successfully.

finalize_plot(xlabel: str, ylabel: str, title: str, ylim: tuple[float, float] | None = None, x_rotation: int = 45, save_dir: str | None = None) -> None

Apply common axis labels, title, limits, and display behavior.

Parameters:

  • xlabel (str) –

    Label for the x-axis.

  • ylabel (str) –

    Label for the y-axis.

  • title (str) –

    Plot title.

  • ylim (tuple[float, float] | None, default: None ) –

    Lower and upper y-axis limits, by default None.

  • x_rotation (int, default: 45 ) –

    Rotation angle for x-axis tick labels, by default 45.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.

ScatterPlot

Bases: SeabornPlot, ABC

Base helper for generating seaborn scatter plots.

Requires

Install the 'pre-defined' extra: pip install luna-bench[pre-defined]

figure: Figure = Figure() class-attribute instance-attribute

The figure this plot is drawn on and the files written from it.

theme: Theme | None = Theme() class-attribute instance-attribute

The seaborn theme the figure is drawn under, and the gridlines behind the marks.

A figure is read against its axis, so it is drawn with the lines that make that possible unless asked otherwise. None takes the theme and the grid away.

missing: Missing = Missing() class-attribute instance-attribute

What becomes of the values the plot cannot draw, and how it says they were there.

option_bundles: dict[str, type[OptionBundle]] = {'style': PlotStyle} class-attribute

Constructor arguments that configure several fields at once, and the bundle each takes.

A style is spread over the bundle fields rather than stored, so a benchmark can hand the same look to every plot while each keeps what it says itself. Applied least specific first: the shared style, then a bundle passed to the plot, then a flat option.

create(*, rows: list[dict[str, Any]], xlabel: str, ylabel: str, title: str, hue: str, x: str = 'x', y: str = 'y', hline: float | None = None, hline_label: str | None = None, hcolor: str = REFERENCE_LINE_COLOUR, save_dir: str | None = None, **kwargs: Any) -> None

Create a scatter plot from row-oriented data.

Parameters:

  • rows (dict[str, Any]) –

    Row-oriented mapping used to construct the plotting DataFrame.

  • xlabel (str) –

    Label for the x-axis.

  • ylabel (str) –

    Label for the y-axis.

  • title (str) –

    Plot title.

  • hue (str) –

    Column used to color points by group.

  • x (str, default: 'x' ) –

    Column name mapped to the x-axis, by default "x".

  • y (str, default: 'y' ) –

    Column name mapped to the y-axis, by default "y".

  • hline (float | None, default: None ) –

    Optional horizontal reference line value, by default None.

  • hline_label (str | None, default: None ) –

    Legend label for the horizontal reference line, by default None.

  • hcolor (str, default: REFERENCE_LINE_COLOUR ) –

    Color of the horizontal reference line, by default black.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments forwarded to :func:seaborn.scatterplot. They override the defaults computed here, so anything seaborn understands (palette, style, size, markers, ...) can be tuned from the call site.

run(benchmark_results: BenchmarkResultContainer, save_dir: str | None = None) -> None abstractmethod

Generate plot output from benchmark results.

Parameters:

draw_into(axes: Axes, benchmark_results: BenchmarkResultContainer) -> None

Run this plot onto an existing axes rather than into a figure of its own.

Everything the plot draws goes through the pyplot state, so making that axes the current one is enough to redirect it. The figure, the files, and the window stay the caller's business - which is what lets several plots share one figure, e.g. the summary grid.

Parameters:

  • axes (Axes) –

    The axes to draw on.

  • benchmark_results (BenchmarkResultContainer) –

    Aggregated benchmark data handed to :meth:run.

resolve_missing(df: pd.DataFrame, column: str, *, by: str | None = None, within: str | None = None) -> tuple[pd.DataFrame, dict[tuple[str, str], int]]

Return the drawable rows and how many of them were not, per category.

A metric with nothing to report says so with a None or an infinity - a time to solution of a run that never reached the optimum is the usual one, since the expected time to something that did not happen is unbounded. Neither is a height a bar can have, and leaving them in poisons the aggregate: one infinity turns the mean of an algorithm into an infinity, and a missing value silently shortens it.

What happens to them is :attr:missing, and by default it is nothing: the plot raises rather than quietly showing a mean over fewer models than it claims. Asked to carry on, it either leaves them out or fills them from the values that could be drawn - Missing(policy="max") puts them just past the tallest bar, which is where "worse than everything here" belongs. Either way they are counted and a warning is logged: a bar resting on half its models is a different statement from one resting on all of them, and that is not visible in the bar itself.

Parameters:

  • df (DataFrame) –

    The plotting data.

  • column (str) –

    Column holding the plotted value.

  • by (str | None, default: None ) –

    Column whose categories the missing values are counted per, e.g. the x-axis of a bar plot. Without one they are counted under "".

  • within (str | None, default: None ) –

    Column that splits those categories further, e.g. the grouping of a bar plot. Counting per group is what lets a figure mark the one bar of a group that lost values rather than the whole category it sits in.

Returns:

  • tuple[DataFrame, dict[tuple[str, str], int]] –

    The rows to draw, and the number of missing values per category and group - the group is "" where there is none. The mapping is empty when nothing was missing.

Raises:

  • PlotMissingValuesError –

    If values are missing and the policy is "raise".

place_legend(axes: Axes, handles: list[Any] | None = None, labels: list[str] | None = None) -> None

Put the legend beside the axes, whatever drew it.

Outside the axes for a figure of its own: a legend inside sits on top of the data, and which corner is free depends on the run rather than on the plot - the figure would move its own key around as the numbers change. Beside it, the key is always in the same place and covers nothing.

A panel of someone else's figure is the exception. The room beside it belongs to the panel next to it, so a key anchored there is drawn over a neighbour rather than over the data; inside the panel it stays within the space the plot was given.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

  • handles (list[Any] | None, default: None ) –

    Legend handles, by default the ones already on the axes.

  • labels (list[str] | None, default: None ) –

    Their labels, by default the ones already on the axes.

note_missing(handles: list[Any], labels: list[str], missing: dict[tuple[str, str], int]) -> None

Add the legend entry that says how many values the figure could not draw.

What a plot can say beyond that depends on what it draws. A bar has a slot of its own to put a cross under, so BarPlot marks the categories themselves; a point in a cloud or a step of a sweep has no slot, and the count in the key is the whole statement there - enough that a filled value is not read as a measured one.

Parameters:

  • handles (list[Any]) –

    Legend handles, extended in place.

  • labels (list[str]) –

    Their labels, extended in place alongside handles.

  • missing (dict[tuple[str, str], int]) –

    Number of missing values per category and group, as counted by :meth:resolve_missing.

apply_theme() -> None

Install the seaborn theme this plot is drawn under, unless it has none.

The theme is matplotlib's global state rather than a property of one figure, so it is installed before the figure is built and left in place afterwards: a benchmark themes its plots by handing every one of them the same Theme, not by each plot putting the previous look back.

apply_grid(axes: Axes) -> None

Draw the gridlines the theme asks for, behind everything else on axes.

Parameters:

  • axes (Axes) –

    The axes the plot was drawn on.

setup_figure() -> None

Create a matplotlib figure, unless the plot is drawing into a shared axes.

save_figure(save_dir: str) -> list[Path]

Write the current figure to save_dir once per configured file format.

Parameters:

  • save_dir (str) –

    Directory to save the figure into. Created if it does not exist.

Returns:

  • list[Path] –

    Paths that were written successfully.

finalize_plot(xlabel: str, ylabel: str, title: str, ylim: tuple[float, float] | None = None, x_rotation: int = 45, save_dir: str | None = None) -> None

Apply common axis labels, title, limits, and display behavior.

Parameters:

  • xlabel (str) –

    Label for the x-axis.

  • ylabel (str) –

    Label for the y-axis.

  • title (str) –

    Plot title.

  • ylim (tuple[float, float] | None, default: None ) –

    Lower and upper y-axis limits, by default None.

  • x_rotation (int, default: 45 ) –

    Rotation angle for x-axis tick labels, by default 45.

  • save_dir (str | None, default: None ) –

    Directory to save the figure into, by default None.