Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit c7e4448

Browse files
authored
Merge pull request #17930 from QuLogic/errorbar-cycler
Fix errorbar property cycling to match plot.
2 parents 5703ac4 + da8d510 commit c7e4448

File tree

6 files changed

+105
-67
lines changed

6 files changed

+105
-67
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
``Axes.errorbar`` cycles non-color properties correctly
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Formerly, `.Axes.errorbar` incorrectly skipped the Axes property cycle if a
5+
color was explicitly specified, even if the property cycler was for other
6+
properties (such as line style). Now, `.Axes.errorbar` will advance the Axes
7+
property cycle as done for `.Axes.plot`, i.e., as long as all properties in the
8+
cycler are not explicitly passed.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
``Axes.errorbar`` cycles non-color properties correctly
2+
-------------------------------------------------------
3+
4+
Formerly, `.Axes.errorbar` incorrectly skipped the Axes property cycle if a
5+
color was explicitly specified, even if the property cycler was for other
6+
properties (such as line style). Now, `.Axes.errorbar` will advance the Axes
7+
property cycle as done for `.Axes.plot`, i.e., as long as all properties in the
8+
cycler are not explicitly passed.
9+
10+
For example, the following will cycle through the line styles:
11+
12+
.. plot::
13+
:include-source: True
14+
15+
x = np.arange(0.1, 4, 0.5)
16+
y = np.exp(-x)
17+
offsets = [0, 1]
18+
19+
plt.rcParams['axes.prop_cycle'] = plt.cycler('linestyle', ['-', '--'])
20+
21+
fig, ax = plt.subplots()
22+
for offset in offsets:
23+
ax.errorbar(x, y + offset, xerr=0.1, yerr=0.3, fmt='tab:blue')

lib/matplotlib/axes/_axes.py

Lines changed: 47 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -3315,35 +3315,8 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33153315

33163316
self._process_unit_info(xdata=x, ydata=y, kwargs=kwargs)
33173317

3318-
plot_line = (fmt.lower() != 'none')
3319-
label = kwargs.pop("label", None)
3320-
3321-
if fmt == '':
3322-
fmt_style_kwargs = {}
3323-
else:
3324-
fmt_style_kwargs = {k: v for k, v in
3325-
zip(('linestyle', 'marker', 'color'),
3326-
_process_plot_format(fmt))
3327-
if v is not None}
3328-
if fmt == 'none':
3329-
# Remove alpha=0 color that _process_plot_format returns
3330-
fmt_style_kwargs.pop('color')
3331-
3332-
if ('color' in kwargs or 'color' in fmt_style_kwargs):
3333-
base_style = {}
3334-
if 'color' in kwargs:
3335-
base_style['color'] = kwargs.pop('color')
3336-
else:
3337-
base_style = next(self._get_lines.prop_cycler)
3338-
3339-
base_style['label'] = '_nolegend_'
3340-
base_style.update(fmt_style_kwargs)
3341-
if 'color' not in base_style:
3342-
base_style['color'] = 'C0'
3343-
if ecolor is None:
3344-
ecolor = base_style['color']
3345-
# make sure all the args are iterable; use lists not arrays to
3346-
# preserve units
3318+
# Make sure all the args are iterable; use lists not arrays to preserve
3319+
# units.
33473320
if not np.iterable(x):
33483321
x = [x]
33493322

@@ -3361,19 +3334,50 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33613334
if not np.iterable(yerr):
33623335
yerr = [yerr] * len(y)
33633336

3364-
# make the style dict for the 'normal' plot line
3365-
plot_line_style = {
3366-
**base_style,
3367-
**kwargs,
3368-
'zorder': (kwargs['zorder'] - .1 if barsabove else
3369-
kwargs['zorder'] + .1),
3370-
}
3337+
label = kwargs.pop("label", None)
3338+
kwargs['label'] = '_nolegend_'
3339+
3340+
# Create the main line and determine overall kwargs for child artists.
3341+
# We avoid calling self.plot() directly, or self._get_lines(), because
3342+
# that would call self._process_unit_info again, and do other indirect
3343+
# data processing.
3344+
(data_line, base_style), = self._get_lines._plot_args(
3345+
(x, y) if fmt == '' else (x, y, fmt), kwargs, return_kwargs=True)
3346+
3347+
# Do this after creating `data_line` to avoid modifying `base_style`.
3348+
if barsabove:
3349+
data_line.set_zorder(kwargs['zorder'] - .1)
3350+
else:
3351+
data_line.set_zorder(kwargs['zorder'] + .1)
3352+
3353+
# Add line to plot, or throw it away and use it to determine kwargs.
3354+
if fmt.lower() != 'none':
3355+
self.add_line(data_line)
3356+
else:
3357+
data_line = None
3358+
# Remove alpha=0 color that _get_lines._plot_args returns for
3359+
# 'none' format, and replace it with user-specified color, if
3360+
# supplied.
3361+
base_style.pop('color')
3362+
if 'color' in kwargs:
3363+
base_style['color'] = kwargs.pop('color')
3364+
3365+
if 'color' not in base_style:
3366+
base_style['color'] = 'C0'
3367+
if ecolor is None:
3368+
ecolor = base_style['color']
33713369

3372-
# make the style dict for the line collections (the bars)
3373-
eb_lines_style = dict(base_style)
3374-
eb_lines_style.pop('marker', None)
3375-
eb_lines_style.pop('linestyle', None)
3376-
eb_lines_style['color'] = ecolor
3370+
# Eject any marker information from line format string, as it's not
3371+
# needed for bars or caps.
3372+
base_style.pop('marker', None)
3373+
base_style.pop('markersize', None)
3374+
base_style.pop('markerfacecolor', None)
3375+
base_style.pop('markeredgewidth', None)
3376+
base_style.pop('markeredgecolor', None)
3377+
base_style.pop('linestyle', None)
3378+
3379+
# Make the style dict for the line collections (the bars).
3380+
eb_lines_style = {**base_style, 'color': ecolor}
33773381

33783382
if elinewidth:
33793383
eb_lines_style['linewidth'] = elinewidth
@@ -3384,15 +3388,8 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33843388
if key in kwargs:
33853389
eb_lines_style[key] = kwargs[key]
33863390

3387-
# set up cap style dictionary
3388-
eb_cap_style = dict(base_style)
3389-
# eject any marker information from format string
3390-
eb_cap_style.pop('marker', None)
3391-
eb_lines_style.pop('markerfacecolor', None)
3392-
eb_lines_style.pop('markeredgewidth', None)
3393-
eb_lines_style.pop('markeredgecolor', None)
3394-
eb_cap_style.pop('ls', None)
3395-
eb_cap_style['linestyle'] = 'none'
3391+
# Make the style dict for the caps.
3392+
eb_cap_style = {**base_style, 'linestyle': 'none'}
33963393
if capsize is None:
33973394
capsize = rcParams["errorbar.capsize"]
33983395
if capsize > 0:
@@ -3408,11 +3405,6 @@ def errorbar(self, x, y, yerr=None, xerr=None,
34083405
eb_cap_style[key] = kwargs[key]
34093406
eb_cap_style['color'] = ecolor
34103407

3411-
data_line = None
3412-
if plot_line:
3413-
data_line = mlines.Line2D(x, y, **plot_line_style)
3414-
self.add_line(data_line)
3415-
34163408
barcols = []
34173409
caplines = []
34183410

lib/matplotlib/axes/_base.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -310,7 +310,7 @@ def _makeline(self, x, y, kw, kwargs):
310310
default_dict = self._getdefaults(set(), kw)
311311
self._setdefaults(default_dict, kw)
312312
seg = mlines.Line2D(x, y, **kw)
313-
return seg
313+
return seg, kw
314314

315315
def _makefill(self, x, y, kw, kwargs):
316316
# Polygon doesn't directly support unitized inputs.
@@ -362,9 +362,9 @@ def _makefill(self, x, y, kw, kwargs):
362362
fill=kwargs.get('fill', True),
363363
closed=kw['closed'])
364364
seg.set(**kwargs)
365-
return seg
365+
return seg, kwargs
366366

367-
def _plot_args(self, tup, kwargs):
367+
def _plot_args(self, tup, kwargs, return_kwargs=False):
368368
if len(tup) > 1 and isinstance(tup[-1], str):
369369
linestyle, marker, color = _process_plot_format(tup[-1])
370370
tup = tup[:-1]
@@ -415,8 +415,12 @@ def _plot_args(self, tup, kwargs):
415415
ncx, ncy = x.shape[1], y.shape[1]
416416
if ncx > 1 and ncy > 1 and ncx != ncy:
417417
raise ValueError(f"x has {ncx} columns but y has {ncy} columns")
418-
return [func(x[:, j % ncx], y[:, j % ncy], kw, kwargs)
419-
for j in range(max(ncx, ncy))]
418+
result = (func(x[:, j % ncx], y[:, j % ncy], kw, kwargs)
419+
for j in range(max(ncx, ncy)))
420+
if return_kwargs:
421+
return list(result)
422+
else:
423+
return [l[0] for l in result]
420424

421425

422426
@cbook._define_aliases({"facecolor": ["fc"]})
Binary file not shown.

lib/matplotlib/tests/test_axes.py

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3147,14 +3147,25 @@ def test_errobar_nonefmt():
31473147
assert np.all(errbar.get_color() == mcolors.to_rgba('C0'))
31483148

31493149

3150-
@image_comparison(['errorbar_with_prop_cycle.png'],
3151-
style='mpl20', remove_text=True)
3152-
def test_errorbar_with_prop_cycle():
3153-
_cycle = cycler(ls=['--', ':'], marker=['s', 's'], mfc=['k', 'w'])
3150+
@check_figures_equal(extensions=['png'])
3151+
def test_errorbar_with_prop_cycle(fig_test, fig_ref):
3152+
ax = fig_ref.subplots()
3153+
ax.errorbar(x=[2, 4, 10], y=[0, 1, 2], yerr=0.5,
3154+
ls='--', marker='s', mfc='k')
3155+
ax.errorbar(x=[2, 4, 10], y=[2, 3, 4], yerr=0.5, color='tab:green',
3156+
ls=':', marker='s', mfc='y')
3157+
ax.errorbar(x=[2, 4, 10], y=[4, 5, 6], yerr=0.5, fmt='tab:blue',
3158+
ls='-.', marker='o', mfc='c')
3159+
ax.set_xlim(1, 11)
3160+
3161+
_cycle = cycler(ls=['--', ':', '-.'], marker=['s', 's', 'o'],
3162+
mfc=['k', 'y', 'c'], color=['b', 'g', 'r'])
31543163
plt.rc("axes", prop_cycle=_cycle)
3155-
fig, ax = plt.subplots()
3156-
ax.errorbar(x=[2, 4, 10], y=[3, 2, 4], yerr=0.5)
3157-
ax.errorbar(x=[2, 4, 10], y=[6, 4, 2], yerr=0.5)
3164+
ax = fig_test.subplots()
3165+
ax.errorbar(x=[2, 4, 10], y=[0, 1, 2], yerr=0.5)
3166+
ax.errorbar(x=[2, 4, 10], y=[2, 3, 4], yerr=0.5, color='tab:green')
3167+
ax.errorbar(x=[2, 4, 10], y=[4, 5, 6], yerr=0.5, fmt='tab:blue')
3168+
ax.set_xlim(1, 11)
31583169

31593170

31603171
@check_figures_equal()

0 commit comments

Comments
 (0)