publiplots.barplot

publiplots.barplot(data, x, y, hue=None, hatch=None, color=None, edgecolor=None, ax=None, title='', xlabel='', ylabel='', linewidth=None, capsize=None, alpha=None, border_radius=None, palette=None, hatch_map=None, legend=True, legend_kws=None, annotate=None, errorbar='se', gap=0.1, order=None, hue_order=None, hatch_order=None, multiple='dodge', stack_by=None, **kwargs)[source]

Create a publication-ready bar plot.

Aggregates long-form data by a categorical axis (x or y) and draws one bar per group, with optional color / hatch grouping and optional stacking. Under the hood multiple="dodge" (the default) wraps seaborn.barplot() and applies publiplots’ palette, hatch, transparency, and legend-stash styling in a post-draw pass; multiple="stack" / "fill" bypasses seaborn (which has no stacked mode) and draws segments directly with ax.bar / ax.barh at integer category positions with cumulative bottom= / left=.

Parameters:
  • data (DataFrame) – Input data in long form — one row per observation.

  • x (str) – Column names for the two axes. Exactly one must be categorical (category dtype, object, or string) and the other numeric. x categorical + y numeric → vertical bars; y categorical + x numeric → horizontal bars. If neither column is categorical, raises ValueError.

  • y (str) – Column names for the two axes. Exactly one must be categorical (category dtype, object, or string) and the other numeric. x categorical + y numeric → vertical bars; y categorical + x numeric → horizontal bars. If neither column is categorical, raises ValueError.

  • hue (str, optional) – Column name for color grouping. When distinct from the categorical axis, bars split within each category — side-by-side under multiple="dodge", stacked under multiple="stack" / "fill". When equal to the categorical axis, hue collapses to per-category coloring (no splitting).

  • hatch (str, optional) –

    Column name driving a second categorical dimension encoded via hatch textures — the texture-based analogue of hue. Under multiple="dodge": when hatch is distinct from both hue and the categorical axis, bars split along a second dodge dimension and the legend renders two entries (see hue-and-hatch double-split in the bar gallery). Under multiple="stack" / "fill": hatch can drive the stack on its own (leave hue unset) — useful for B&W-friendly stacks — or share the column with hue (hatch=hue) to pattern each stack segment by its hue level. When both hue and hatch are distinct non-categorical columns, pass stack_by="hue" or stack_by="hatch" to pick which dimension stacks; the other is dodged side-by-side within each category.

    Patterns are drawn from publiplots.HATCH_PATTERNS by default; density is controlled globally via publiplots.set_hatch_mode() ("dense", "sparse", or "off") or overridden per-plot with hatch_map. Call publiplots.list_hatch_patterns() to see the catalog.

  • color (str, optional) – Fixed color for all bars when no hue split is active. Accepts any matplotlib color string ("#ff0000", "red", etc.). Falls back to rcParams["color"].

  • edgecolor (str, optional) – Explicit edge color override. When None (the default, also the default of rcParams["edgecolor"]), the edge matches the face color — publiplots’ double-layer transparent-fill styling. Set to a specific color ("black") to draw bars with consistent outlines regardless of fill.

  • ax (Axes, optional) – Matplotlib axes to draw on. If None, creates a new figure via publiplots.layout.subplots() so the publiplots layout manager can keep geometry stable. To control axes dimensions, pre-create with pp.subplots(axes_size=(w_mm, h_mm)) and pass ax=.

  • title (str or None, default="") – Plot title. Pass None to leave the existing title unchanged.

  • xlabel (str or None, default="") – Axis labels. Pass None to leave the existing label unchanged.

  • ylabel (str or None, default="") – Axis labels. Pass None to leave the existing label unchanged.

  • linewidth (float, optional) – Width of bar edges and hatch strokes. Defaults to rcParams["lines.linewidth"].

  • capsize (float, optional) – Width of errorbar caps in data units (dodge path only — stacked bars drop errorbars). Defaults to rcParams["capsize"].

  • alpha (float, optional) – Transparency of the bar fill (edges stay fully opaque). 0 = outlined bars only; 1 = solid fill. Defaults to rcParams["alpha"].

  • border_radius (float or (top_mm, bottom_mm) tuple, optional) – Corner radius for the bars, in millimeters (print-consistent, independent of data-axis range). A scalar rounds all four corners symmetrically; a 2-tuple rounds the top and bottom independently, enabling infographic-style bars with rounded tops and flat baselines (border_radius=(1.5, 0)). Defaults to the bar.border_radius rcParam ((0, 0) = flat). Set globally via pp.rcParams['bar.border_radius'] = 1.5.

  • palette (str, dict, or list, optional) –

    Color palette. Can be:

    • str: seaborn palette name or publiplots palette name

    • dict: explicit {hue_level: color} mapping

    • list: list of colors assigned in hue-level order

    Only used when a hue split is active.

  • hatch_map (dict, optional) –

    Mapping from hatch-column values to matplotlib hatch-pattern strings, overriding the per-hatch_mode defaults. Matplotlib hatches interpret: / \ | - + x o O . *; repeating a glyph increases density (e.g. "///").

    Example (two-level hatch with a plain control group):

    hatch_map={"control": "", "treated": "///"}
    

    publiplots.HATCH_PATTERNS lists the built-in patterns the hatch_mode resolver picks from; call publiplots.list_hatch_patterns() to print them.

  • legend (bool or dict, default=True) – Whether to stash & render the legend. Accepts bool or dict[kind, bool] for per-kind control (e.g. legend={"hatch": False} hides just the hatch entry in a double-split bar plot). False suppresses stashing entirely, so a figure-level publiplots.legend() group won’t claim any entries from this axes.

  • legend_kws (dict, optional) – Extra kwargs forwarded to the legend builder. Notable keys: inside=True with loc=... places the legend inside the axes using matplotlib’s corner-based placement; hue_label / hatch_label override the title shown above each entry.

  • annotate (bool or dict, optional) –

    If truthy, label each bar with its aggregated value. Pass a dict to forward options to publiplots.annotate(): fmt (Python format spec), anchor ("outside"/"inside"/"top"/"left"/"right"), offset (mm), color, rotation (degrees).

    Under multiple="stack" / "fill" the default anchor is "inside" and one label is produced per drawn segment; under multiple="dodge" the default anchor is "outside" and one label is produced per bar.

  • errorbar (str or None, default="se") – Error bar type for the dodge path: "se" (standard error), "sd" (standard deviation), "ci" (confidence interval), tuple ("pi", q) for percentile intervals, callable, or None for no error bars. Under multiple="stack" / "fill" errorbars are always dropped with a UserWarning — per-segment errors are not additive without covariance info.

  • gap (float, default=0.1) – Gap between adjacent bars, as a fraction of bar width. Under "dodge" this is the gap between hue/hatch levels within a category; under "stack" / "fill" this is the gap between stacks across the categorical axis.

  • order (list, optional) – Order of the categorical axis. Determines both draw order and the tick-label sequence.

  • hue_order (list, optional) – Explicit order of hue levels. Under "stack" / "fill", also determines stack order bottom-to-top (first level at the base, last level on top) and the legend entry order.

  • hatch_order (list, optional) – Order of hatch levels. Under "stack" / "fill" with hatch driving the stack, determines stack order bottom-to-top.

  • multiple ({"dodge", "stack", "fill", "gain"}, default="dodge") –

    How to arrange bars across the secondary (hue / hatch) categorical dimension within each category of the primary axis.

    • "dodge" (default): levels sit side-by-side within each category via seaborn’s dodging. Current behavior, unchanged — supports errorbars, hue + hatch double-split, and the full four-case legend dispatcher.

    • "stack": levels sit on top of each other within each category, each segment’s base set to the cumulative height (or width, horizontal) of the levels below. Drawn directly with ax.bar / ax.barh — seaborn’s barplot has no stack mode.

    • "fill": stack and then normalize so every stack sums to 1.0 (100%-stacked bars — good for showing proportions whose totals differ across categories).

    • "gain": pairwise comparison for exactly 2 levels. Per category: the bottom segment shows min of the two values (colored by the losing level); the top segment shows max - min (colored by the winning level). Bar top = max. Who wins can flip across categories — the base color flips accordingly. Use for summary metrics that don’t compose by sum (AUC, accuracy, F1); for additive quantities use "stack". Ties render as a single bar in the first level’s color. Missing one of the two levels at some category raises ValueError.

    Stacking requires at least one of hue / hatch to be set and distinct from the categorical axis. If neither is set, raises ValueError. When both are distinct non-categorical columns, use stack_by= (below) to pick the stack dimension — the other is dodged side-by-side within each category. hue == hatch (both drive a single stack, patterns overlaid on colored swatches) is allowed.

  • stack_by ({"hue", "hatch"}, optional) – Required when multiple in {"stack", "fill"} and both hue and hatch are distinct non-categorical columns. Picks which dimension accumulates along the value axis; the other dimension dodges side-by-side within each category (so each category shows N_other dodged stacks, each of N_stack_by segments). Ignored when only one of hue / hatch is active. Pass None in the single-dim case — the sole split dimension becomes the stack.

  • **kwargs – Additional keyword arguments passed to seaborn.barplot() in the dodge path. Ignored in the stack/fill path. figsize is always rejected — use pp.subplots(axes_size=...) and pass ax=.

Returns:

The axes where the plot was drawn. Recover the figure handle with ax.get_figure().

Return type:

Axes

Raises:
  • ValueError – If neither x nor y is categorical; if multiple is not one of "dodge", "stack", "fill", "gain"; if multiple="stack"|"fill"|"gain" is requested without a stack column (hue / hatch); if multiple="stack"|"fill"|"gain" is requested with both hue and hatch set as distinct non-categorical columns but no stack_by= was provided; if multiple="gain" is requested with a stack column that has ≠ 2 levels or a level missing at some category.

  • TypeError – If figsize is passed (publiplots owns figure geometry via publiplots.subplots()).

Warns:

UserWarning – If errorbar is set under multiple="stack"|"fill"; the errorbar is silently dropped (per-segment errors are not additive without covariance information).

Notes

Performance. The dodge path delegates aggregation to seaborn; the stack/fill path aggregates once in a single pass via the shared publiplots.annotate._splits.BarSplitSpec iterator (the same deterministic draw order used for annotate pairing).

Legend stash. Every call stashes LegendEntry objects on the axes (unless legend=False); per-axes legends render unless a figure-level publiplots.legend() group claims them.

Examples

Simple bar plot:

>>> ax = pp.barplot(data=df, x="category", y="value")

Color grouping via hue:

>>> ax = pp.barplot(data=df, x="time", y="value",
...                 hue="group", palette="pastel")

Two categorical dimensions via hue + hatch (side-by-side):

>>> ax = pp.barplot(
...     data=df, x="cell_type", y="viability",
...     hue="treatment", hatch="time",
...     palette={"Vehicle": "#8E8EC1", "Drug": "#60a8a8"},
...     hatch_map={"24h": "", "48h": "///"},
... )

Value labels on each bar:

>>> ax = pp.barplot(data=df, x="category", y="value",
...                 annotate={"fmt": ".2f"})

Stacked bars (multiple="stack") with per-segment labels:

>>> ax = pp.barplot(
...     data=df, x="cohort", y="count", hue="stage",
...     multiple="stack", errorbar=None,
...     hue_order=["Early", "Mid", "Late"],
...     annotate={"fmt": ".0f"},
... )

100%-stacked proportions with percentage labels:

>>> ax = pp.barplot(
...     data=df, x="cohort", y="count", hue="stage",
...     multiple="fill", errorbar=None,
...     annotate={"fmt": ".0%"},
... )

B&W-friendly stack keyed by hatch (no hue):

>>> ax = pp.barplot(
...     data=df, x="cohort", y="count", hatch="stage",
...     multiple="stack", errorbar=None, color="#5D83C3",
...     hatch_map={"Early": "", "Mid": "//", "Late": "xx"},
... )

Horizontal stacked bars:

>>> ax = pp.barplot(
...     data=df, x="count", y="cohort", hue="stage",
...     multiple="stack", errorbar=None,
... )

Gain / improvement bars (pairwise comparison of 2 levels):

>>> ax = pp.barplot(
...     data=df, x="metric", y="score", hue="model",
...     multiple="gain", errorbar=None,
...     palette={"Baseline": "#8E8EC1", "Proposed": "#60a8a8"},
...     annotate={"fmt": ".2f"},
... )

Two categoricals: stack one, dodge the other (stack_by):

>>> ax = pp.barplot(
...     data=df, x="cell_type", y="viability",
...     hue="treatment", hatch="time",
...     multiple="stack", stack_by="hue", errorbar=None,
...     palette={"Vehicle": "#8E8EC1", "Drug": "#60a8a8"},
...     hatch_map={"24h": "", "48h": "///"},
... )

See also

publiplots.histplot

Histogram plot (also supports multiple="stack"/"fill" via seaborn).

publiplots.set_hatch_mode

Set the global hatch-density mode.

publiplots.list_hatch_patterns

Print the built-in hatch patterns.

publiplots.annotate

Add value labels to bars (see annotate= above for the subset promoted to pp.barplot).

publiplots.legend

Figure-level legend group that claims stashed entries across multiple axes.