Skip to content

Show error bars in legend by default (consistency between barstd/boxstd and shadestd/fadestd) #703

@kinyatoride

Description

@kinyatoride

When plotting means with different uncertainty kwargs, the legend is inconsistent:

  • barstd / boxstd → only the mean line shows up in the legend.
  • shadestd / fadestd → band + line both show up, overlaid.

I'd suggest showing all four in the legend by default. That way the legend glyph matches what's drawn in the plot, regardless of which kwarg was used. Matplotlib's native errorbar() already renders caps in the legend, so this would also align with the underlying behavior.

Users who prefer a line-only legend can still get it easily — each handle is a tuple (error_artist..., mean_line):

h = ax.plot(data, means=True, shadestd=1, label='label')
ax.legend([t[-1] if isinstance(t, tuple) else t for t in h])

Reproducer

import numpy as np, pandas as pd, ultraplot as uplt

state = np.random.RandomState(51423)
data = state.rand(20, 8).cumsum(axis=0).cumsum(axis=1)[:, ::-1]
data = data + 20 * state.normal(size=(20, 8)) + 30
data = pd.DataFrame(data, columns=np.arange(0, 16, 2))

fig, axs = uplt.subplots(ncols=4)
for ax, kw in zip(axs, [{}, {'barstd': 1}, {'boxstd': 1}, {'shadestd': 1}]):
    h = ax.plot(data, means=True, label='label', **kw)
    ax.legend(h)

Where it happens

ErrorbarContainer is explicitly filtered out of legends in ultraplot/axes/base.py (~line 2130):

ignore = (mcontainer.ErrorbarContainer,)
...
if isinstance(obj, ignore) and not _legend_label(obj):
    continue

Both barstd and boxstd go through self.errorbar(...) (plot.py ~3310, 3323), so both get dropped. Shade/fade use fill_betweenPolyCollection and aren't filtered. Dropping or narrowing the filter should be enough — the HandlerTuple path for shade/fade already works nicely and bars/boxes would flow through the same way.

Thanks!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions