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_between → PolyCollection 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!
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):Reproducer
Where it happens
ErrorbarContaineris explicitly filtered out of legends inultraplot/axes/base.py(~line 2130):Both
barstdandboxstdgo throughself.errorbar(...)(plot.py~3310, 3323), so both get dropped. Shade/fade usefill_between→PolyCollectionand aren't filtered. Dropping or narrowing the filter should be enough — theHandlerTuplepath for shade/fade already works nicely and bars/boxes would flow through the same way.Thanks!