diff --git a/aaanalysis/__init__.py b/aaanalysis/__init__.py index 4fc1edee..282b97a8 100644 --- a/aaanalysis/__init__.py +++ b/aaanalysis/__init__.py @@ -9,7 +9,8 @@ from .explainable_ai import TreeModel from .protein_design import AAMut, AAMutPlot, SeqMut, SeqMutPlot, SeqOpt, SeqOptPlot from .plotting import (plot_get_clist, plot_get_cmap, plot_get_cdict, - plot_settings, plot_legend, plot_gcfs, plot_rank) + plot_settings, plot_legend, plot_gcfs, plot_rank, + plot_comparison) from .metrics import (comp_auc_adjusted, comp_bic_score, comp_kld, comp_per_protein_ap, comp_detection_metrics, comp_bootstrap_ci, comp_smooth_scores) @@ -65,6 +66,7 @@ "plot_legend", "plot_gcfs", "plot_rank", + "plot_comparison", "comp_auc_adjusted", "comp_bic_score", "comp_kld", diff --git a/aaanalysis/plotting/__init__.py b/aaanalysis/plotting/__init__.py index df692aad..bdc2ff63 100644 --- a/aaanalysis/plotting/__init__.py +++ b/aaanalysis/plotting/__init__.py @@ -2,7 +2,7 @@ Plotting utilities: shared styling, colors, and legends for every ``*Plot`` class. Public objects: plot_get_clist, plot_get_cmap, plot_get_cdict, plot_settings, -plot_legend, plot_gcfs, plot_rank. +plot_legend, plot_gcfs, plot_rank, plot_comparison. A cross-cutting subpackage (not a pipeline stage): ``plot_settings`` sets the house rcParams, the ``plot_get_*`` helpers supply the color list / map / dict, and ``plot_legend`` / ``plot_gcfs`` / ``plot_rank`` are reused by the paired plot classes @@ -18,6 +18,7 @@ from ._plot_gcfs import plot_gcfs from ._plot_legend import plot_legend from ._plot_rank import plot_rank +from ._plot_comparison import plot_comparison __all__ = [ @@ -28,4 +29,5 @@ "plot_legend", "plot_gcfs", "plot_rank", + "plot_comparison", ] diff --git a/aaanalysis/plotting/_plot_comparison.py b/aaanalysis/plotting/_plot_comparison.py new file mode 100644 index 00000000..a8501272 --- /dev/null +++ b/aaanalysis/plotting/_plot_comparison.py @@ -0,0 +1,263 @@ +""" +This is a script for the frontend of the plot_comparison function — a grouped +method x condition comparison barplot with value labels and a chance/baseline line. +""" +from typing import Optional, List, Dict, Union, Tuple +import numpy as np +import pandas as pd +import seaborn as sns +from matplotlib import pyplot as plt +from matplotlib.axes import Axes +from matplotlib.figure import Figure + +from aaanalysis import utils as ut +from ._plot_get_clist import plot_get_clist + + +# I Helper Functions +def _resolve_order(values: List, order: Optional[List], name: str) -> List: + """First-appearance order by default; otherwise validate the explicit order covers all values.""" + seen = list(dict.fromkeys(values)) + if order is None: + return seen + ut.check_list_like(name=name, val=order) + seen_set = set(seen) + missing = seen_set - set(order) + if missing: + raise ValueError(f"'{name}' is missing values present in the data: {sorted(map(str, missing))}") + # Keep only the values that actually occur, in the requested order, de-duplicated + # (a repeated entry would otherwise create a duplicate grid axis label). + return [g for g in dict.fromkeys(order) if g in seen_set] + + +def _resolve_colors(group_order: List, colors: Optional[Union[List, Dict]]) -> Dict: + """Build a {group: color} map: explicit list/dict wins, else the house categorical palette.""" + n = len(group_order) + if colors is None: + palette = plot_get_clist(n_colors=max(n, 2)) + return {g: palette[i] for i, g in enumerate(group_order)} + if isinstance(colors, dict): + missing = [g for g in group_order if g not in colors] + if missing: + raise ValueError(f"'colors' dict is missing colors for groups: {missing}") + return {g: colors[g] for g in group_order} + ut.check_list_like(name="colors", val=colors) + if len(colors) < n: + raise ValueError(f"'colors' (n={len(colors)}) should provide at least one color " + f"per group (n_groups={n}).") + return {g: colors[i] for i, g in enumerate(group_order)} + + +def _auto_annotation_fmt(values: np.ndarray) -> str: + """Pick a value-label format: no decimals for integers, else 2 decimals for small + (fractional, e.g. AUC in [0, 1]) values and 1 decimal for larger scales.""" + vals = values[~np.isnan(values)] + if vals.size == 0 or np.allclose(vals, np.round(vals)): + return "{:.0f}" + if float(np.max(np.abs(vals))) < 10: + return "{:.2f}" + return "{:.1f}" + + +# II Main Functions +def plot_comparison(df_eval: pd.DataFrame, + group: str = "group", + condition: str = "condition", + value: str = "value", + baseline: Optional[Union[int, float]] = 50, + baseline_label: Optional[str] = None, + annotate: bool = True, + annotation_fmt: Optional[str] = None, + group_order: Optional[List[str]] = None, + condition_order: Optional[List[str]] = None, + colors: Optional[Union[List[str], Dict[str, str]]] = None, + bar_width: Union[int, float] = 0.8, + ax: Optional[Axes] = None, + figsize: Tuple[Union[int, float], Union[int, float]] = (7, 4.2), + xlabel: Optional[str] = None, + ylabel: str = "Score", + title: Optional[str] = None, + ylim: Optional[Tuple[Union[int, float], Union[int, float]]] = None, + fontsize_annotations: Union[int, float] = 10, + xtick_rotation: Union[int, float] = 0, + ) -> Tuple[Figure, Axes]: + """ + Plot a grouped method x condition comparison barplot with value labels and a baseline line. + + Draws the recurring "benchmark result" figure from a tidy eval frame in one call: + each ``condition`` is a cluster on the x-axis, each ``group`` a colored bar within it + (auto offsets / widths for *N* groups), with optional per-bar value labels and an + optional dashed chance / baseline line. Replaces the manual ``x ± w/2`` offsets, the + per-bar ``ax.text`` annotation loop, and the manual ``axhline`` of a hand-built grouped + barplot. + + .. versionadded:: 1.1.0 + + Parameters + ---------- + df_eval : pd.DataFrame, shape (n_rows, n_cols) + Tidy (long-form) evaluation frame with one row per (``group``, ``condition``) + cell; must contain the ``group``, ``condition``, and ``value`` columns. Repeated + (``group``, ``condition``) rows are averaged. + group : str, default="group" + Column whose distinct values become the colored bars within each cluster (the legend). + condition : str, default="condition" + Column whose distinct values become the x-axis clusters. + value : str, default="value" + Column with the numeric bar heights (e.g. balanced accuracy in percent). + baseline : int or float, optional + y-value of a dashed horizontal chance / baseline line. If ``None``, no line is drawn. + baseline_label : str, optional + Legend label for the baseline line. If ``None`` and ``baseline`` is set, a label + ``"chance ()"`` is generated; pass ``""`` to draw the line without a + legend entry. Placing it in the legend keeps it from overlapping the bars. + annotate : bool, default=True + If ``True``, write each bar's value above it. + annotation_fmt : str, optional + Python format string for the value labels (e.g. ``"{:.1f}"``). If ``None``, it is + chosen from the data: no decimals for integer-valued scores, two decimals for + fractional metrics (e.g. AUC in ``[0, 1]``), one decimal otherwise. + group_order : list of str, optional + Order of the groups (bar order within each cluster). Defaults to first-appearance order. + condition_order : list of str, optional + Order of the conditions (x-axis clusters). Defaults to first-appearance order. + colors : list of str or dict, optional + Bar colors: a list aligned to ``group_order``, or a ``group -> color`` dict. + Defaults to the house categorical palette (:func:`plot_get_clist`). + bar_width : int or float, default=0.8 + Total width of each condition cluster (split evenly across the groups). Must be in (0, 1]. + ax : matplotlib.axes.Axes, optional + Axes to draw on. If ``None``, a new figure and axes are created. + figsize : tuple, default=(7, 4.2) + Figure size when ``ax`` is ``None``. + xlabel : str, optional + x-axis label (none by default; the cluster tick labels carry the conditions). + ylabel : str, default="Score" + y-axis label. + title : str, optional + Axes title. + ylim : tuple, optional + y-axis limits ``(bottom, top)``. If ``None``, the top is extended above the tallest + bar / baseline to leave room for the value labels. + fontsize_annotations : int or float, default=10 + Font size of the per-bar value labels. + xtick_rotation : int or float, default=0 + Rotation (degrees) of the x-axis cluster tick labels; use e.g. ``30`` to keep + long ``condition`` names from overlapping. Rotated labels are right-aligned. + + Returns + ------- + fig : matplotlib.figure.Figure + The figure. + ax : matplotlib.axes.Axes + The axes with the grouped comparison barplot. + + See Also + -------- + * :func:`aaanalysis.plot_get_clist` for the house categorical palette. + * :func:`aaanalysis.plot_rank` for a per-protein rank scatter of a deployed predictor. + * :func:`aaanalysis.pipe.plot_eval` for a viridis evaluation-grid view of a wider sweep. + + Examples + -------- + .. include:: examples/plot_comparison.rst + """ + # Check input + ut.check_str(name="group", val=group) + ut.check_str(name="condition", val=condition) + ut.check_str(name="value", val=value) + if len({group, condition, value}) < 3: + raise ValueError(f"'group', 'condition', and 'value' should be three distinct columns, " + f"got group={group!r}, condition={condition!r}, value={value!r}.") + ut.check_df(name="df_eval", df=df_eval, cols_required=[group, condition, value]) + if len(df_eval) == 0: + raise ValueError("'df_eval' (0 rows) should contain at least one row.") + if not pd.api.types.is_numeric_dtype(df_eval[value]): + raise ValueError(f"'{value}' column of 'df_eval' should be numeric " + f"(the bar heights), got dtype '{df_eval[value].dtype}'.") + ut.check_number_val(name="baseline", val=baseline, accept_none=True, just_int=False) + ut.check_str(name="baseline_label", val=baseline_label, accept_none=True) + ut.check_bool(name="annotate", val=annotate) + ut.check_str(name="annotation_fmt", val=annotation_fmt, accept_none=True) + ut.check_list_like(name="group_order", val=group_order, accept_none=True) + ut.check_list_like(name="condition_order", val=condition_order, accept_none=True) + ut.check_number_range(name="bar_width", val=bar_width, min_val=0, max_val=1, just_int=False) + if bar_width == 0: + raise ValueError("'bar_width' should be greater than 0.") + ut.check_ax(ax=ax, accept_none=True) + ut.check_figsize(figsize=figsize, accept_none=True) + ut.check_str(name="xlabel", val=xlabel, accept_none=True) + ut.check_str(name="ylabel", val=ylabel, accept_none=True) + ut.check_str(name="title", val=title, accept_none=True) + ut.check_number_range(name="fontsize_annotations", val=fontsize_annotations, min_val=0, + just_int=False) + ut.check_number_val(name="xtick_rotation", val=xtick_rotation, just_int=False) + if ylim is not None: + ut.check_lim(name="ylim", val=ylim) + + # Resolve orders + colors + group_order = _resolve_order(df_eval[group].tolist(), group_order, "group_order") + condition_order = _resolve_order(df_eval[condition].tolist(), condition_order, "condition_order") + dict_group_color = _resolve_colors(group_order, colors) + + # Build the (group x condition) value grid (mean over any repeated cells) + grid = (df_eval.groupby([group, condition])[value].mean() + .unstack(condition) + .reindex(index=group_order, columns=condition_order)) + + # Draw + if ax is None: + fig, ax = plt.subplots(figsize=figsize) + else: + fig = ax.figure + n_groups = len(group_order) + n_cond = len(condition_order) + x = np.arange(n_cond) + each_w = bar_width / n_groups + heights_max = float(np.nanmax(grid.values)) if grid.size else 0.0 + label_pad = 0.01 * max(heights_max, 1) # loop-invariant gap between a bar top and its label + if annotation_fmt is None: + annotation_fmt = _auto_annotation_fmt(grid.values) + for j, g in enumerate(group_order): + offset = (j - (n_groups - 1) / 2) * each_w + heights = grid.loc[g].to_numpy(dtype=float) + bars = ax.bar(x + offset, heights, each_w, label=str(g), color=dict_group_color[g]) + if annotate: + for b, h in zip(bars, heights): + if np.isnan(h): + continue + ax.text(b.get_x() + b.get_width() / 2, h + label_pad, + annotation_fmt.format(h), ha="center", va="bottom", + fontsize=fontsize_annotations, weight="bold") + + # Baseline / chance line (labelled in the legend so it never overlaps the bars) + if baseline is not None: + if baseline_label is None: + baseline_label = f"chance ({baseline:g})" + ax.axhline(baseline, ls="--", color="black", lw=1, + label=baseline_label if baseline_label != "" else "_nolegend_") + + # Cosmetics + ax.set_xticks(x) + if xtick_rotation: + ax.set_xticklabels(condition_order, rotation=xtick_rotation, ha="right", + rotation_mode="anchor") + else: + ax.set_xticklabels(condition_order) + if xlabel is not None: + ax.set_xlabel(xlabel) + ax.set_ylabel(ylabel) + if title is not None: + ax.set_title(title) + if ylim is not None: + ax.set_ylim(ylim) + elif heights_max > 0: + top = heights_max if baseline is None else max(heights_max, baseline) + ax.set_ylim(top=top * (1.15 if annotate else 1.05)) + # Legend outside the axes (upper right): a grouped barplot fills the plot area, so an + # inside legend would overlap the tallest bars / their value labels. + ax.legend(loc="upper left", bbox_to_anchor=(1.01, 1.0), frameon=False) + sns.despine(ax=ax) + # ``ax.figure`` is typed ``Figure | SubFigure | None`` by the matplotlib stubs, + # but a top-level Axes here always belongs to a real Figure. + return fig, ax # pyright: ignore[reportReturnType] diff --git a/docs/_cheatsheet/content.py b/docs/_cheatsheet/content.py index 801adc7f..f6c65fb7 100644 --- a/docs/_cheatsheet/content.py +++ b/docs/_cheatsheet/content.py @@ -230,6 +230,7 @@ ("BIC score · KL divergence", "comp_bic_score(X, labels) · comp_kld", None), ("Per-protein / detection (v1.1)", "comp_per_protein_ap · comp_detection_metrics", None), ("Plot style, fonts & standalone legend", "plot_settings(font_scale) · plot_legend(ax)", None), + ("Grouped comparison barplot + chance line", "plot_comparison(df_eval, baseline=50)", None, "v1.1"), ]}, {"name": "Protein Design", "tag": "mutations · design", "under_construction": True, diff --git a/docs/source/api.rst b/docs/source/api.rst index 6b8adb47..21d1fe4d 100755 --- a/docs/source/api.rst +++ b/docs/source/api.rst @@ -124,6 +124,7 @@ Utility Functions comp_smooth_scores display_df options + plot_comparison plot_gcfs plot_get_cdict plot_get_clist diff --git a/docs/source/index/release_notes.rst b/docs/source/index/release_notes.rst index 89fa7d74..c15bff45 100644 --- a/docs/source/index/release_notes.rst +++ b/docs/source/index/release_notes.rst @@ -191,6 +191,10 @@ Added - :func:`~aaanalysis.plot_rank`: Standalone per-protein max-score-vs-rank scatter with group coloring and optional threshold lines (pairs with the new ``aa.metrics`` functions). +- **plot_comparison**: Grouped method × condition comparison barplot from a tidy eval + frame, with automatic bar offsets / widths for *N* groups, optional per-bar value + labels, and an optional dashed chance / baseline line (replaces hand-built grouped + barplots with manual offsets, ``ax.text`` loops, and ``axhline``). **Golden Pipelines** diff --git a/examples/plotting/plot_comparison.ipynb b/examples/plotting/plot_comparison.ipynb new file mode 100644 index 00000000..6ab8b59b --- /dev/null +++ b/examples/plotting/plot_comparison.ipynb @@ -0,0 +1,224 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "b5f4c5c7", + "metadata": {}, + "source": [ + "The ``aa.plot_comparison()`` function draws the recurring \"benchmark result\" figure from a tidy (long-form) evaluation frame: each ``condition`` becomes an x-axis cluster, each ``group`` a colored bar within it (with automatic bar offsets and widths for *N* groups), with per-bar value labels and an optional dashed chance / baseline line. One call replaces the manual ``x ± w/2`` offsets, the per-bar ``ax.text`` loop, and the manual ``axhline`` of a hand-built grouped barplot." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "24e447bf", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-01T03:30:09.242260Z", + "iopub.status.busy": "2026-07-01T03:30:09.242073Z", + "iopub.status.idle": "2026-07-01T03:30:10.968437Z", + "shell.execute_reply": "2026-07-01T03:30:10.968228Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "DataFrame shape: (6, 3)\n" + ] + }, + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 groupconditionvalue
1Scale-basedNo expansion61
2Scale-basedRandom60
3Scale-baseddPULearn75
4CPPNo expansion71
5CPPRandom74
6CPPdPULearn94
\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import pandas as pd\n", + "import matplotlib.pyplot as plt\n", + "import aaanalysis as aa\n", + "\n", + "aa.options[\"verbose\"] = False\n", + "\n", + "# Tidy eval frame: one row per (group, condition) cell.\n", + "df_eval = pd.DataFrame({\n", + " \"group\": [\"Scale-based\"] * 3 + [\"CPP\"] * 3,\n", + " \"condition\": [\"No expansion\", \"Random\", \"dPULearn\"] * 2,\n", + " \"value\": [61, 60, 75, 71, 74, 94],\n", + "})\n", + "aa.display_df(df_eval, n_rows=10, show_shape=True)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "a0efdae9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-01T03:30:10.969494Z", + "iopub.status.busy": "2026-07-01T03:30:10.969405Z", + "iopub.status.idle": "2026-07-01T03:30:11.030314Z", + "shell.execute_reply": "2026-07-01T03:30:11.030103Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "aa.plot_settings()\n", + "fig, ax = aa.plot_comparison(df_eval=df_eval, baseline=50,\n", + " ylabel=\"Balanced accuracy [%]\",\n", + " title=\"Feature engineering x data expansion\")\n", + "plt.tight_layout()\n", + "plt.show()" + ] + }, + { + "cell_type": "markdown", + "id": "36eb7fc7", + "metadata": {}, + "source": [ + "**Further parameters.** ``plot_comparison`` also accepts: ``group`` / ``condition`` / ``value`` — the column names of the tidy frame; ``baseline_label`` — the legend label for the baseline line (auto ``\"chance (50)\"`` by default, ``\"\"`` to hide); ``annotate`` — toggle the per-bar value labels; ``annotation_fmt`` — format string for the value labels (auto-selected precision by default); ``group_order`` / ``condition_order`` — explicit bar / cluster order; ``colors`` — a list aligned to the groups or a ``group -> color`` dict (defaults to the house palette of ``aa.plot_get_clist``); ``bar_width`` — total cluster width; ``xtick_rotation`` — degrees to rotate the cluster tick labels (e.g. ``30`` for long ``condition`` names); ``figsize``; ``xlabel``; ``ylim``; ``fontsize_annotations``; and ``ax`` to draw onto an existing axes." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "98ded14f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-01T03:30:11.031407Z", + "iopub.status.busy": "2026-07-01T03:30:11.031328Z", + "iopub.status.idle": "2026-07-01T03:30:11.074072Z", + "shell.execute_reply": "2026-07-01T03:30:11.073813Z" + } + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAApcAAAGCCAYAAAC1n+gkAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjksIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvJkbTWQAAAAlwSFlzAAAPYQAAD2EBqD+naQAAfCJJREFUeJztnQe8zfX/xz/2zs4KIVt2kpmGRJs0tUR7aUqlIUoDDf1oaaCEBkkKpSGKkhUSKXuPsp3/4/n+/T7n/73nnnNd17mXe+7r+Xgc5/iu8/1+vt9zv6/ve2YLhUIhJ4QQQgghRBzIHo+NCCGEEEIIIXEphBBCCCHiiiyXQgghhBAibkhcCiGEEEKIuCFxKYQQQggh4obEpRBCCCGEiBsSl0IIIYQQIm5IXAohhBBCiLghcZkB/Pvvv2727Nn2LoQQQgiRyEhcZgC//faba9Sokb0LIYQQQiQyEpdCCCGEECJuSFwKIYQQQoi4IXEphBBCCCHihsSlEEIIIYSIGxKXQgghhBAibkhcCiGEEEKIuCFxKYQQQggh4obEpRBCCCGEiBsSl0IIIYQQIm5IXAohhBBCiLghcSmEEEIIIeKGxKUQQgghhMja4nLJkiWuQIEC7s4774y5zJdffunOPPNMd+yxx7qCBQu6k046yb322msuFApFXX7fvn1u6NChrlGjRu6YY45xxYoVc+3atXNTp05NxyMRQgghhEgsMp24XLt2rTv//PPdv//+G3OZwYMHm7CcNm2aa9CggWvTpo1bsGCB69atm7vmmmuSLX/gwAHXpUsXd8MNN7hly5a5M844w5144onuiy++cKeffrp7/fXX0/mohBBCCCESg0wlLn/55RfXokULt3DhwpjLLFq0yN12222uSJEibubMme7zzz9348aNs3WqVKni3n77bTdq1Kgk67zxxhvuvffecw0bNnRLly51Y8eOdV9//bWbNGmSy5Mnj7v11lvdihUrMuAIhRBCCCEyN5lCXG7evNndf//9rmnTpu733393lSpVirns008/bZbIe++919WrVy88vUKFCu7ll1+2z88++2ySdfr162fvL774oitatGh4OlZLXO+7du1yL730UjocmRBCCCFEFhSXiLX0eqWGQYMGuf79+7uSJUu6Tz75xF111VUxlx0/fry9d+zYMdk83N1YNH/88UdzrwPu8j/++MOV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LnLjxo1z+/fvj++eCiGEEFkAjDdLly61zyeffHLM5UqWLOkuuOAC+zxjxowM2z8h0k1cNmvWzA0dOtStWbPGjRgxwrVt29biQoj94GIvV66cu/fee938+fPT+hVCCCFEliNXrlzhz+PHj09x2ccee8zusyNHjkw2b86cOa579+4Wq0kiUNGiRd3pp5/uRo8eHXVbJIvceeed5o0keYj9KFGihDvttNPcq6++eshGI77/6quvdhUqVHB58uRxxYsXNxf+mDFj3OFC3CkGrtKlS9uxYcl96KGH3JYtW6IuT8LRO++8484991zTJ6xTsGBBV61aNXfjjTe6xYsXR13v888/d+edd55ZiP14nHrqqea5JfckGtu2bXNPPPGEa9CggStUqJArUKCAO/HEEy2GNtb+AWEPl112mSVsEQ7BOi+//LJpq8xGtlAoFIrXxtauXWsnjxfWTPuCbNlco0aN3HXXXWeDVrhwYZfV4IJhDIhPVdC1EInFwho13dFKzd8WuqMVJfSkDBnR3333nd1Dr7rqKruHNm/ePJl7PBaIH4Ti3r17TeAgorhH//333zb/wQcfDLve4eOPP3aXXHKJ2717t4muypUrm6hZtmyZ++eff2yZyy+/3A0fPjxJtnibNm3sM9+TM+f/5wgjiu644w4TpGyvatWqbuPGjW7FihXhbb399tupPh5/zSCmEcu7du1yK1eutONCKM6bN8/29/jjj7f9qlixYng9Muo7dOjgpk6dav9nGUQi40HMKiAAv/nmGxOEHkINOAYoW7asvQhBIHsbEJlffvllkmP47bffLJOfZZjOviIUeQBA4LJfJGLVqFEjybExrtdee62NIw8BrMc2+D6+h2OCJUuWuBNOOMFlqVJEpUqVcvfcc489rWDSJ8uNE4aouuWWWywJiB8JYksIIYQQ0XnxxRdNlGH/eeutt1zr1q1dsWLFTCQ99dRT5gaPZdH6/vvvLeEWofLAAw+4devWuZ9++smE1BtvvOGyZ8/u+vbt67744ouwGx5hg7C86aabTHRxH8dIxGdKIQFeytR4Iz/77DP7fsTVoEGDzFrHfZ9EJMQYVkC2ldYHDPQFVsNp06a5RYsW2b4uXLjQrJcIsi5duiRZ/umnnzZhiaCcOXOmCeYff/zRhC7/R5sgoBkTD/t8//3322eswghZ1mFdrJkIRgQfyVYetoFl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uOVy3LhxJixxi1NuiO43dL2hluS7775ryyxYsMAyxr/77jtzjwsRqyWfEEIIIbK4uKRzDR1uEJZAi7X69eubkPTQ4Yb6eCRtDBw40D4LEa0ln0gbqrEohBAiYdziZJtWrFgxybSaNWu6bdu2WWceD5ZLauV5a5UQ0WosCiGEECKLi8vChQsnc3VXrlzZ3onFDEKiBhnjQvgai75ouhBCCCESizSLS1zedORZt25deNoJJ5xgST0///xzMjGRkX3FxdENVu/IXs9BvvrqK7uO1PpRCCGEyEIxl2effbb75ptvLOOX3uK4xE866SSbRzHsG264wRJ8xo4da27yOnXqxHO/hRBCZGKO5njhQ6HmbwvT/TsIN6N82yeffGI1o6m2QVgR3sLTTz/dupvRcjmSYEnASHLlyuUKFixo22jbtq274447XKlSpZIsM2zYMHfttddGXZ9t58mTx3Iv6tat6y699FLLwSCeXog0i8ubbrrJDR482JIzaCOHNQr3d+vWrd20adPsQicmc86cOXYRnnvuuRptIYQQ4hAYP368CbwNGzaEG05wz920aZObP3++iU2aUlAL1jeliKRq1aqW+xBk7969to3Zs2dbR7T//Oc/7rPPPnMnn3xy1G34msQevEu7du0y4xHr8UIAU0kmX758OsdZnMOKuaReYatWrexi5wnGWy2Jp+PJiouWEkW4y++///547rcQQgiR0Dz33HNmmEFYdu7c2c2bNy98b12+fLklR958880mFHv16uUefvjhqNtBdH777bdJXjNmzHBLliyx1ru0Zt68ebPVqKbJRTQi16cyDKKU0LgBAwbYMmiCPn36pOuYiMzBYQVC8jREfBwXvAeLJf/v16+f6969u3v++efdTz/95I455ph47K8QQgiR8CDgvFHmkUcesfa4tWvXTrJMmTJl3MsvvxwWlX379jXBdyjg0sYoBDQ7wVJ6KJBPceedd1rrXsACitgVWZs0u8Xvu+8+E5JXXXVVMnN7yZIlZanMAhytMVMZEQMlhBDpBS5njDN4/po2beoee+yxFJd/6KGHLD4SSyYGneHDhx9yDgUxnLi5Sbbs2LHjIe/zhRde6D744ANztS9evDiZEBZZizSLSzrzYD7HjJ47d+747pUQQgiRha2WCxf+9yE5NSFl3IPfeOMN+3zKKacc8veRF1GoUCETl9u3b09zqJwnrdsQiUPOw8le48kkf/788d0jIYQQIgvz5Zdf2juZ16eddlqq1jnjjDPS/H1bt261koFQvnz5NG2D+E1PWrchEoc0x1w2atTILVq0yK1duza+eySEEEJkYXwjEjqZZUS+Aq50T4cOHQ55fbyYVI8B2vqWK1curvsnspC4fO211+yib9mypbnIKahOOaIDBw7EfAkhhBAiZYhb9PkL6QWCENc7WeYkAsEll1zi6tevn+q4UCyeZIgTs0mcJe51knmFSLNb/LrrrjOXOKKSzweDi27fvn0acSGEECIFChQoYO/xyrqmTmasYugeSh7RECUWKRVkB8oRDho0yISmEGkWl8H2fTzBCCGEEOLwocQQ+MLph0u0IupkhxcpUsS657Vv3941adIkxW1EFlGnBBEiuHTp0taql1JEkd8hsi5pFpdTp06N754IIYQQwlWvXt1G4e+//zbXczATOxYI0R07dlicZrQi6tdcc81hZ7ALke7ikjaPQgghhIgv559/vrvrrruszuWUKVOshuTBwKWNiMRKSSMTlQgUmbZDjxBCCCHiS6VKlcI9vp955pmDhp7t2bMnHC9Zs2ZNCUuReS2XNKg/VOjmIzKGp556yr300ktWzJbSErTkCpa0ILibjg4//vijxcsIIYQ4ehg4cKBr1qyZmz59unvyySetC08sKLS+bNkyi4OM1V9ciEwhLonfOFj2mIenLpaVuMwYnn76adezZ09XsWJFV6FCBTdy5EgLvPZPtjwYUD5KCCHE0QltH/k7TpkgBOOCBQusbFCwreLy5ctNdPp2j71795axQGRucYloiSUuqXe5ZcsWKz3EMsSPFCxY8HD2U6QSSlcgLsngmzt3rmUE0gP+l19+MSvmvffe64YMGaLxFEKIoxwslsWLF3f33XefGQl48bedDjibN2+2UoBAfOUTTzxhywmRqWMueWLCDB/ttW7dOmsP+d5779kPY/Xq1eG+pyJ9+fXXX+2PTosWLaxXbK5cueyc4P5eunSpCUvc5HRYEukfmnDcccdZpufll19uvwmgsxXnh7pwxEf5Vm9CCBFJjx49rNg5hoGTTjrJ+n/Pnj3buuNR8Pyee+4xq6aEpUgIy+XBwGLWuXNnq6PVrl07C0omk02kL4h+b8FEwCA2zzrrLIu5LFasmHv//fetHlmbNm10Ko5AaMIrr7ximZ+0d6Ou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cVjicurUqRpBIYQQQggRH3HZunXrtK4qhBBCCCESFGXYCCGEEEKII2+59CxcuNDNnTs33P4xyL59+2z633//7SZMmOAWLFhwuF8nhBBCCCESUVwiJK+++mo3YsSIgy5Lwk+2bNnS+lVCCCGEECLR3eJvvPGGGz58uAlHSg2VKlXKPhcpUsSVKVPGpvks8vr167vXX389nvsthBBCCCESSVyOHDnSrJEUU//nn3/c4sWLTVB26NDB3ODbtm1zQ4YMcXny5HGrVq2yIusZzbvvvutOPfVUV7RoUauzWb58eXfNNde4RYsWRV1+1KhRrnnz5q5YsWKucOHCVstzzJgxGb7fQgghhBBZTlz++uuvVjS9b9++LkeOHK5gwYKuTp064RJFiLlu3bq5/v37u3Xr1rkXXnjBZRRYTK+44gprUfn999+7WrVqufbt27ucOXO6t956yzVs2NBNnjw5yTr33Xefu+SSS9ycOXNMYJ588slWy7NTp07ukUceybB9F0IIIYTIkuJy69atrlKlSi5fvnzhabVr1zYr5YYNG8LTEJgIz/Hjx7uMAnc9saBly5Z1s2fPdt9995376KOP3O+//+569eplSUaITyyu8OWXX7pnnnnGVaxY0ZKOxo0b5yZNmuR++uknV6JECffEE0+4GTNmZNj+CyGEEEJkOXGJ1TKyV3iVKlXCGeQe3OInnHCC++OPP1xG8dprr9n7U089ZdZUDxZWhCIieO3atSYqAeurf69QoUJ4+bp167o+ffrY5+eeey7D9l8IIYQQIsuJS6x8CMZdu3aFp1WuXNlc0pQmCrJ79257ZRTEWNasWdO1aNEi2TziRKtXr26fsbLSG33atGkWL3reeeclW75jx462DqWUIkstCSGEEEKIOIlLEmV27Njh7r333rDoqlevnr3jkvaZ4r/99psl+xx33HEuo/jwww/NvY3bPpL9+/e7WbNm2WcSfFiOaSyL+z4S3OJkwuNCX7p0aYbsvxBCCCFElhOXt912myXtDB482FzJWCZxI+Manz59ujvrrLPcPffc404//XQTn82aNXNHA+zvn3/+aaLxtNNOcytXrrTp5cqVi7kOpZVg9erVGbafQgghhBBZSlwiIt977z0r2YNrmdhKGDRokLmRycYeMGCACTKSfo6GjOspU6aYpdXHY+bPnz+c1MPnWPikJSy1sUBcU34p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fvqpmzRpkjvmmGNs7M877zyNvxBCRCBxKZKxZs0a98Ybb7gLL7zQrJW33HKLO/nkk12NGjXcjh073KhRo1yJEiXc008/7a699lqNYBwFDRbJaBayt956y917771uw4YN4WklS5Z0o0ePdi1btpQFM07jzysY/uH57LPP7IFq3rx59n+W4Vw9/vjj7oYbbrBzIYQQ4r8o5jKLx1FCZAzZ0KFD3UMPPeTuvPNO9+CDD7rbb7/dffDBB+61115z7733nt1MsWh+8sknbvXq1UfgCBLrXPhEKSyVXlhOmzbNrGRbt241QfPoo4+6SpUquTfffNN99NFH7oorrnDr1693zzzzTHhdcXjngTEMCss+ffq4evXquV9++cU98cQTJux79+7txo4d63r06GHnavDgwW7ixIkaeiGECCDLZYKxYMECd8IJJ7jcuXMnm+fj9ILs2bMnvKx3xS5atMjVr1/fXIIImu+//94VLFjQ7du3z6ZhzbzxxhtN+PznP/9x55xzToYdXyKzc+dON2jQIDdgwAATjtCuXTu3a9cut23bNvfdd9+5PHnyhK3LZcuWtc+Iz1q1ah3RfU+EOEqs8liCly9fbmL+oosuMiF/4okn2rXPgxWf/bJYLZ999lnXuXNnC1eIZvEUQoisiP4aJhAdO3Z0derUcW+//XZUi6S/+c2aNcvdddddrkOHDu62224zC826devC1q9q1aq5M844w/3zzz+uWbNmSYQlcINlOlbLb7/9NsOPM1Hg/GzatMnc3RMmTLCxxELG+F922WVmNcMq9tVXX1kSFcKSdXggKF26tIkfGD9+/JE+lEyJF5Zr1651P/30k437ddddZ2Ef27dvt5hi/8B2yimnhIUlopTfBDGv5cqVc9OnT7fflBBCiP8icZkAIPygSZMm9o5FMZqrdO7cue7iiy+2RAQsjoiWYcOGuUceecS1bds2LEpZ75JLLrHPXjwGLTxFihRxzZs3txssVs0lS5Zk0JEmVggC40xyznPPPWcCs3v37u7yyy83cTN8+HBzv5K4A+XLlw+v48/FVVddZe8sv3v37gw+ssxDrPJNv/32mytevLgJRxLWWK5///72uVChQnaNUxWB6cQcRz6kcW54yCLRavLkyRl2PEIIcdQTEpmeAwcO2Pvu3btDy5YtSzZ///79oV27doWuvPLKULZs2ULnnHNO6OOP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6t5//33XsmVL215WLB+UXtSuXdu1bt3a7d69240dOzZmmRtPo0aN3OjRo91VV11lpZyyMosWLbLyQYwJ4wj79++36/+YY45x99xzj0177733bEwrV67smjdvbtO++eYb+31wnaflevYloYQQQogjfjfwN79Iok2LhBvmH3/8YXUMq1WrZtM++OAD17ZtW1eiRAnXvXt3e7/22mvdxRdf7IoVKxYWJ9S4nDhxoitatKitywthiUilHh9ipUuXLvYZsVOjRg339ddfu/nz59s2WJ9999sT8QEx3759e/v80UcfJam7KFJm48aNVju1UKFCdl3zG2LsvOirV6+e/TZWrlzpJk2aZNOo1UotTGqKzpgxw6alRVzqNyCEECJDxCXWwP79+4eLJkfD3/w2bNhg4o2C5FitUmMFoTBzzZo1TeRhbUSYXHLJJe7XX391nTp1cq+88oqJzddff92Kna9YsSK8LjdXijcjHCnSjSUTIfryyy+bVfLqq692Dz/8sMufP799B0Wk2ccff/zRCj1TGFqiJ/4gUjgnjPv3339v4y1SBw9AQOFzivfzG/JC0T8I+WL1w4cPt/c6deq4k046yayW/P5AFkghhBBHpbjEKkjXmeeff97EXjRrJP/HWtimTRtXoUIF69KBsKBryvjx4008RlvPw42TTiPwyy+/mADs16+fWbzo8nHDDTeYe7BXr1727t2ssGzZMlufbiMIGu9S5f2iiy5yb775pll6+G5utojXfPny2TYQwCL9wF1L+MHgwYNdw4YNFXaQSgjbIFSDhyZ+fxBpXWc84cMPPzQByoMV3ajwAuAaX7x4sc1XqIcQQoijRlz6mxLWvgsvvNBaLXLTiuY6e+qpp1zHjh1NfNIKrlu3buae/vnnn03gPfvss//dyRhWTG6mxJfxTpu5vn37uvvvv981bdrUWjACVtMffvjBPtPOEbipDhw40D6fd955UbeNqOTG7PcZ9yHfhfAtU6ZMXMZKxG4HyQPBjTfeaNZhuVxTBw9G/nrGWg9Y2P0DEhCjzDSs7z7s4MQTTzRXOsJyzZo1Nk1jLoQQIs2kMREoXNoHopUsIeuUtntkalO0nGzV4LK+fRy1Iin34zN9qSFJiSDmUa8yWmeb4HYWLVpkreVY/sEHH0zWmWTAgAFW649OIb7TCLUAhw0bFu5WklqUDSuOdigBRHMAfg/UZeU34Pn9999DderUCdd3pRMPsAzlivhdCCGEEIfLIYvL+fPnW51HCo8PGjQoxWURjYg6lp08eXJYdHITa9eunQnPESNGhJcP9h2m5A83QDp/+NaKsaAcjS+cTtcRRC3lVvgOL2C5eUZDglEkGnTI8W0y6R7F/ymYTlkuCs7TOYpyWtRo1fUvhBDiiItL+nMHO3Fcd9111ps4GmvXrrXC5ixHv2cP1kn6DVPPkGXA3+R8nT3EKMWda9WqFVqwYEHM/fFidNy4cVY03Xcc8YXRr7nmmtDPP/+cZNnIdYVIJPAq0ESA4v/UsPS/hwoVKkRtiSqEEELEk2z8cyhudLJKKdNDtjXxcMQlkm1KDT3K/fzP1R6O2Xr11Vfd7bffbrGKJNmULVvWyp6Q7EMsGBnlJM5EQgkg6kvOnj3bTZkyxZ166qkH3TcSGSizwjqUHWId4veEyIqsX7/eSnVRz5XENX5PQgghRHqT81BXQLRRzgRxSYIMNfImT55sJYDmzp1rIpPMUw9JMLVq1TJBSWkZSgRRMojM6z///NPK+0SCYKUUDZngCEVfQiiYmBANkhIox+JLsvhtgcoGiawEv5WSJUvai+YAQgghxFGbLU6mKeWCKNKMpfDRRx91DzzwgAnGPn36WBHyadOmhZdHfFLqhI45PmscKwqlgeie8+2339p24H9u+iTZ2uCtoIdSf8+XL0JUSliKrIZqVQohhMhUpYgQh9SkxO1GfUlKAI0YMcKmjxkzxkoQDR061JZFhNJiDmsmlktcdNCuXTsTpLTu++6772yaL/2DGFy+fLnVlMSN7ju2HNKBqRWdEEIIIUTmEJcIRepSwsiRI83aeM4551gNvXPPPdd6clOj8LbbbnNLly41cVm3bl1rnYjAhDPPPNNc6Uzr0aOH+/LLL80qimClqw71+vj/Qw895IoXLx62YgohhBBCiKOXQ07o8SxatMgEJnGTU6dONde3T8R5++23rQg2bu/69etbtxXiJu+9917r102SDwWfV69ebYk9uMuxNJ5wwglW9JxCzlgwWZ4X8ZdCCCGEECKBO/SUL1/eEnuwKPpOH/v27TMhSNtFLJF0taH7zhVXXGFtHnF3k9hDBx6g0w1u8SFDhri2bdu6AgUK2Ouaa65x06dPd71795awFEIIIYTICpZL+OSTT9wFF1xg2eAzZswwYeiTcrBErl271j399NPulVdecbt377Z1SpQoYQlAuMKDJYtg1apVFmMZJHIZIYQQQgiRoL3FGzRo4Bo2bGhJOlgqg+WCEIWlSpWy/uEffvih9f4GSg9hxdyyZUsS0cjyXlhiDfXZ3hKWQgghhBBZRFwiHn0mNwIymKXtRWGuXLksM3zChAmW7IMrnEQe6lwGCYpI3OfK9hZCCCGEyGJucaCmJbGXZHTPnDnTrI+Rrmxvzdy1a5c65gghhBBCJDCHZbmEmjVrWlF14iUnTpxo07xLO/wl/7Nm+laMJP4cpqYVQgghhBCJKC5pB0lBdaCQOhysIw71KxVLKYQQQgiReBxyb/FIEJLEXfJOLKUQQgghhMi6HHbMpRBCCCGEEHFzi3vQqJGxlkIIIYQQImshy6UQQgghhDj6LJdCCCGEEEJIXAohhBBCiLghcSmEEEIIIeKGxKUQQgghhIgbEpdCCCGEECJuSFwKIYQQQoi4IXEphBBCCCHihsSlEEIIIYSIGxKXQgghhBAibkhcCiGEEEKIuCFxKYQQQgghXLz4PxfWOu2k+qGzAAAAAElFTkSuQmCC", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "aa.plot_settings()\n", + "fig, ax = aa.plot_comparison(df_eval=df_eval, baseline=50, baseline_label=\"random (50%)\",\n", + " colors={\"Scale-based\": \"tab:gray\", \"CPP\": \"tab:red\"},\n", + " group_order=[\"Scale-based\", \"CPP\"], xtick_rotation=20,\n", + " ylabel=\"Balanced accuracy [%]\", ylim=(0, 108))\n", + "plt.tight_layout()\n", + "plt.show()" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.14.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/unit/plotting_tests/test_plot_comparison.py b/tests/unit/plotting_tests/test_plot_comparison.py new file mode 100644 index 00000000..4cdfd76d --- /dev/null +++ b/tests/unit/plotting_tests/test_plot_comparison.py @@ -0,0 +1,253 @@ +"""This is a script to test the plot_comparison() grouped comparison barplot (#311).""" +import matplotlib +matplotlib.use("Agg") +import matplotlib.pyplot as plt +import pandas as pd +import pytest + +import aaanalysis as aa +from aaanalysis.plotting import plot_comparison + +aa.options["verbose"] = False + + +# Helper functions +def _df(groups=("Scale-based", "CPP"), conditions=("No expansion", "Random", "dPULearn")): + rows = [] + base = {"Scale-based": 60, "CPP": 80} + for g in groups: + for k, c in enumerate(conditions): + rows.append({"group": g, "condition": c, "value": base.get(g, 70) + k}) + return pd.DataFrame(rows) + + +@pytest.fixture(autouse=True) +def _close_figs(): + yield + plt.close("all") + + +class TestPlotComparison: + """Normal cases for plot_comparison.""" + + def test_returns_fig_ax(self): + fig, ax = plot_comparison(df_eval=_df()) + assert isinstance(fig, plt.Figure) and isinstance(ax, plt.Axes) + + def test_bar_count_groups_times_conditions(self): + # 2 groups x 3 conditions = 6 bars + fig, ax = plot_comparison(df_eval=_df()) + assert len(ax.patches) == 6 + + def test_bar_count_three_groups(self): + fig, ax = plot_comparison(df_eval=_df(groups=("A", "B", "C"))) + assert len(ax.patches) == 9 + + def test_baseline_drawn_when_set(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=50) + assert len(ax.lines) == 1 + + def test_baseline_none_no_line(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=None) + assert len(ax.lines) == 0 + + def test_baseline_label_default_text(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=50) + assert any("chance" in t.get_text() for t in ax.get_legend().get_texts()) + + def test_baseline_label_custom(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=50, baseline_label="random (50%)") + assert any("random (50%)" == t.get_text() for t in ax.get_legend().get_texts()) + + def test_baseline_label_empty_suppresses_legend_entry(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=50, baseline_label="") + # Only the groups appear in the legend, no baseline entry. + assert [t.get_text() for t in ax.get_legend().get_texts()] == ["Scale-based", "CPP"] + + def test_annotate_true_writes_value_labels(self): + fig, ax = plot_comparison(df_eval=_df(), annotate=True, baseline=None) + assert len(ax.texts) == 6 + + def test_annotate_false_no_value_labels(self): + fig, ax = plot_comparison(df_eval=_df(), annotate=False, baseline=None) + assert len(ax.texts) == 0 + + def test_integer_values_labelled_without_decimals(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=None) + assert all("." not in t.get_text() for t in ax.texts) + + def test_fractional_values_keep_precision(self): + # AUC-like values in [0, 1] must NOT collapse to "0"/"1" integer labels. + df = pd.DataFrame([{"group": g, "condition": c, "value": v} + for g, c, v in [("A", "x", 0.62), ("A", "y", 0.71), + ("B", "x", 0.83), ("B", "y", 0.90)]]) + fig, ax = plot_comparison(df_eval=df, baseline=None) + labels = sorted(t.get_text() for t in ax.texts) + assert labels == ["0.62", "0.71", "0.83", "0.90"] + + def test_annotation_fmt_override(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=None, annotation_fmt="{:.1f}") + assert all("." in t.get_text() for t in ax.texts) + assert any(t.get_text() == "60.0" for t in ax.texts) + + def test_legend_labels_are_groups(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=None) + labels = [t.get_text() for t in ax.get_legend().get_texts()] + assert set(labels) == {"Scale-based", "CPP"} + + def test_group_order_controls_bar_order(self): + fig, ax = plot_comparison(df_eval=_df(), baseline=None, group_order=["CPP", "Scale-based"]) + labels = [t.get_text() for t in ax.get_legend().get_texts()] + assert labels == ["CPP", "Scale-based"] + + def test_condition_order_controls_ticks(self): + order = ["dPULearn", "Random", "No expansion"] + fig, ax = plot_comparison(df_eval=_df(), condition_order=order) + assert [t.get_text() for t in ax.get_xticklabels()] == order + + def test_colors_list(self): + fig, ax = plot_comparison(df_eval=_df(), colors=["tab:gray", "tab:red"]) + assert len(ax.patches) == 6 + + def test_colors_dict(self): + fig, ax = plot_comparison(df_eval=_df(), colors={"Scale-based": "tab:gray", "CPP": "tab:red"}) + assert len(ax.patches) == 6 + + def test_custom_columns(self): + df = _df().rename(columns={"group": "method", "condition": "setting", "value": "acc"}) + fig, ax = plot_comparison(df_eval=df, group="method", condition="setting", value="acc") + assert len(ax.patches) == 6 + + def test_aggregates_repeated_cells(self): + df = pd.concat([_df(), _df()], ignore_index=True) # duplicate (group, condition) rows + fig, ax = plot_comparison(df_eval=df) + assert len(ax.patches) == 6 + + def test_repeated_cells_use_mean(self): + # Two rows for the same (group, condition) with different values -> the mean. + df = pd.DataFrame([{"group": "A", "condition": "x", "value": 60}, + {"group": "A", "condition": "x", "value": 80}]) + fig, ax = plot_comparison(df_eval=df, baseline=None) + assert ax.patches[0].get_height() == pytest.approx(70) + + def test_passing_existing_ax(self): + fig_in, ax_in = plt.subplots() + fig_out, ax_out = plot_comparison(df_eval=_df(), ax=ax_in) + assert ax_out is ax_in and fig_out is fig_in + + def test_labels_and_title(self): + fig, ax = plot_comparison(df_eval=_df(), xlabel="X", ylabel="Balanced accuracy [%]", + title="Bench") + assert ax.get_xlabel() == "X" + assert ax.get_ylabel() == "Balanced accuracy [%]" + assert ax.get_title() == "Bench" + + def test_duplicate_group_order_deduped(self): + # A repeated entry in the explicit order must not create a duplicate bar row. + fig, ax = plot_comparison(df_eval=_df(), baseline=None, + group_order=["Scale-based", "Scale-based", "CPP"]) + assert len(ax.patches) == 6 + labels = [t.get_text() for t in ax.get_legend().get_texts()] + assert labels == ["Scale-based", "CPP"] + + def test_duplicate_condition_order_deduped(self): + fig, ax = plot_comparison(df_eval=_df(), + condition_order=["No expansion", "No expansion", "Random", "dPULearn"]) + assert len(ax.patches) == 6 + assert [t.get_text() for t in ax.get_xticklabels()] == ["No expansion", "Random", "dPULearn"] + + def test_missing_cell_partial_grid(self): + # A (group, condition) combination absent from df_eval leaves a gap, no crash / label. + df = pd.DataFrame([{"group": "A", "condition": "x", "value": 60}, + {"group": "B", "condition": "y", "value": 70}]) + fig, ax = plot_comparison(df_eval=df, baseline=None) + assert len(ax.patches) == 4 # 2 groups x 2 conditions + assert len(ax.texts) == 2 # only the 2 present cells are labelled + + def test_ylim_respected(self): + fig, ax = plot_comparison(df_eval=_df(), ylim=(0, 108)) + assert ax.get_ylim() == (0, 108) + + def test_bar_width_and_figsize_and_fontsize(self): + fig, ax = plot_comparison(df_eval=_df(), bar_width=0.6, figsize=(8, 5), + fontsize_annotations=8) + assert len(ax.patches) == 6 + + def test_xtick_rotation_applied(self): + fig, ax = plot_comparison(df_eval=_df(), xtick_rotation=30) + assert all(t.get_rotation() == 30 for t in ax.get_xticklabels()) + + def test_xtick_rotation_default_horizontal(self): + fig, ax = plot_comparison(df_eval=_df()) + assert all(t.get_rotation() == 0 for t in ax.get_xticklabels()) + + +class TestPlotComparisonErrors: + """Negative cases: bad input must raise ValueError.""" + + def test_missing_column_raises(self): + df = _df().drop(columns=["value"]) + with pytest.raises(ValueError): + plot_comparison(df_eval=df) + + def test_empty_df_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df().iloc[0:0]) + + def test_non_df_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=[1, 2, 3]) + + def test_bad_group_type_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), group=123) + + def test_duplicate_columns_raise(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), group="group", condition="group") + + def test_bad_baseline_type_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), baseline="50") + + def test_bad_annotate_type_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), annotate="yes") + + def test_bad_annotation_fmt_type_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), annotation_fmt=2) + + def test_bar_width_out_of_range_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), bar_width=1.5) + + def test_bar_width_zero_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), bar_width=0) + + def test_incomplete_group_order_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), group_order=["CPP"]) + + def test_colors_list_too_short_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), colors=["tab:gray"]) + + def test_colors_dict_missing_group_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), colors={"CPP": "tab:red"}) + + def test_bad_ylim_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), ylim=(10,)) + + def test_non_numeric_value_column_raises(self): + df = _df() + df["value"] = df["value"].astype(str) + with pytest.raises(ValueError): + plot_comparison(df_eval=df) + + def test_bad_xtick_rotation_type_raises(self): + with pytest.raises(ValueError): + plot_comparison(df_eval=_df(), xtick_rotation="45")