Skip to content

Commit 371b9ab

Browse files
authored
Merge pull request #18556 from QuLogic/errorevery
ENH: Accept same types to errorevery as markevery
2 parents 64950dd + fda57b0 commit 371b9ab

File tree

3 files changed

+90
-15
lines changed

3 files changed

+90
-15
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
``errorbar`` *errorevery* parameter matches *markevery*
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
4+
Similar to the *markevery* parameter to `~.Axes.plot`, the *errorevery*
5+
parameter of `~.Axes.errorbar` now accept slices and NumPy fancy indexes (which
6+
must match the size of *x*).
7+
8+
.. plot::
9+
10+
x = np.linspace(0, 1, 15)
11+
y = x * (1-x)
12+
yerr = y/6
13+
14+
fig, ax = plt.subplots(2, constrained_layout=True)
15+
ax[0].errorbar(x, y, yerr, capsize=2)
16+
ax[0].set_title('errorevery unspecified')
17+
18+
ax[1].errorbar(x, y, yerr, capsize=2,
19+
errorevery=[False, True, True, False, True] * 3)
20+
ax[1].set_title('errorevery=[False, True, True, False, True] * 3')

lib/matplotlib/axes/_axes.py

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import itertools
33
import logging
44
import math
5-
from numbers import Number
5+
from numbers import Integral, Number
66

77
import numpy as np
88
from numpy import ma
@@ -3147,17 +3147,6 @@ def errorbar(self, x, y, yerr=None, xerr=None,
31473147
kwargs = {k: v for k, v in kwargs.items() if v is not None}
31483148
kwargs.setdefault('zorder', 2)
31493149

3150-
try:
3151-
offset, errorevery = errorevery
3152-
except TypeError:
3153-
offset = 0
3154-
3155-
if errorevery < 1 or int(errorevery) != errorevery:
3156-
raise ValueError(
3157-
'errorevery must be positive integer or tuple of integers')
3158-
if int(offset) != offset:
3159-
raise ValueError("errorevery's starting index must be an integer")
3160-
31613150
self._process_unit_info([("x", x), ("y", y)], kwargs, convert=False)
31623151

31633152
# Make sure all the args are iterable; use lists not arrays to preserve
@@ -3179,6 +3168,33 @@ def errorbar(self, x, y, yerr=None, xerr=None,
31793168
if not np.iterable(yerr):
31803169
yerr = [yerr] * len(y)
31813170

3171+
if isinstance(errorevery, Integral):
3172+
errorevery = (0, errorevery)
3173+
if isinstance(errorevery, tuple):
3174+
if (len(errorevery) == 2 and
3175+
isinstance(errorevery[0], Integral) and
3176+
isinstance(errorevery[1], Integral)):
3177+
errorevery = slice(errorevery[0], None, errorevery[1])
3178+
else:
3179+
raise ValueError(
3180+
f'errorevery={errorevery!r} is a not a tuple of two '
3181+
f'integers')
3182+
3183+
elif isinstance(errorevery, slice):
3184+
pass
3185+
3186+
elif not isinstance(errorevery, str) and np.iterable(errorevery):
3187+
# fancy indexing
3188+
try:
3189+
x[errorevery]
3190+
except (ValueError, IndexError) as err:
3191+
raise ValueError(
3192+
f"errorevery={errorevery!r} is iterable but not a valid "
3193+
f"NumPy fancy index to match 'xerr'/'yerr'") from err
3194+
else:
3195+
raise ValueError(
3196+
f"errorevery={errorevery!r} is not a recognized value")
3197+
31823198
label = kwargs.pop("label", None)
31833199
kwargs['label'] = '_nolegend_'
31843200

@@ -3219,6 +3235,7 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32193235
base_style.pop('markerfacecolor', None)
32203236
base_style.pop('markeredgewidth', None)
32213237
base_style.pop('markeredgecolor', None)
3238+
base_style.pop('markevery', None)
32223239
base_style.pop('linestyle', None)
32233240

32243241
# Make the style dict for the line collections (the bars).
@@ -3260,7 +3277,7 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32603277
xuplims = np.broadcast_to(xuplims, len(x)).astype(bool)
32613278

32623279
everymask = np.zeros(len(x), bool)
3263-
everymask[offset::errorevery] = True
3280+
everymask[errorevery] = True
32643281

32653282
def apply_mask(arrays, mask):
32663283
# Return, for each array in *arrays*, the elements for which *mask*

lib/matplotlib/tests/test_axes.py

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3373,8 +3373,25 @@ def test_errorbar_with_prop_cycle(fig_test, fig_ref):
33733373
ax.set_xlim(1, 11)
33743374

33753375

3376+
def test_errorbar_every_invalid():
3377+
x = np.linspace(0, 1, 15)
3378+
y = x * (1-x)
3379+
yerr = y/6
3380+
3381+
ax = plt.figure().subplots()
3382+
3383+
with pytest.raises(ValueError, match='not a tuple of two integers'):
3384+
ax.errorbar(x, y, yerr, errorevery=(1, 2, 3))
3385+
with pytest.raises(ValueError, match='not a tuple of two integers'):
3386+
ax.errorbar(x, y, yerr, errorevery=(1.3, 3))
3387+
with pytest.raises(ValueError, match='not a valid NumPy fancy index'):
3388+
ax.errorbar(x, y, yerr, errorevery=[False, True])
3389+
with pytest.raises(ValueError, match='not a recognized value'):
3390+
ax.errorbar(x, y, yerr, errorevery='foobar')
3391+
3392+
33763393
@check_figures_equal()
3377-
def test_errorbar_offsets(fig_test, fig_ref):
3394+
def test_errorbar_every(fig_test, fig_ref):
33783395
x = np.linspace(0, 1, 15)
33793396
y = x * (1-x)
33803397
yerr = y/6
@@ -3385,7 +3402,7 @@ def test_errorbar_offsets(fig_test, fig_ref):
33853402
for color, shift in zip('rgbk', [0, 0, 2, 7]):
33863403
y += .02
33873404

3388-
# Using feature in question
3405+
# Check errorevery using an explicit offset and step.
33893406
ax_test.errorbar(x, y, yerr, errorevery=(shift, 4),
33903407
capsize=4, c=color)
33913408

@@ -3395,6 +3412,27 @@ def test_errorbar_offsets(fig_test, fig_ref):
33953412
ax_ref.errorbar(x[shift::4], y[shift::4], yerr[shift::4],
33963413
capsize=4, c=color, fmt='none')
33973414

3415+
# Check that markevery is propagated to line, without affecting errorbars.
3416+
ax_test.errorbar(x, y + 0.1, yerr, markevery=(1, 4), capsize=4, fmt='o')
3417+
ax_ref.plot(x[1::4], y[1::4] + 0.1, 'o', zorder=2.1)
3418+
ax_ref.errorbar(x, y + 0.1, yerr, capsize=4, fmt='none')
3419+
3420+
# Check that passing a slice to markevery/errorevery works.
3421+
ax_test.errorbar(x, y + 0.2, yerr, errorevery=slice(2, None, 3),
3422+
markevery=slice(2, None, 3),
3423+
capsize=4, c='C0', fmt='o')
3424+
ax_ref.plot(x[2::3], y[2::3] + 0.2, 'o', c='C0', zorder=2.1)
3425+
ax_ref.errorbar(x[2::3], y[2::3] + 0.2, yerr[2::3],
3426+
capsize=4, c='C0', fmt='none')
3427+
3428+
# Check that passing an iterable to markevery/errorevery works.
3429+
ax_test.errorbar(x, y + 0.2, yerr, errorevery=[False, True, False] * 5,
3430+
markevery=[False, True, False] * 5,
3431+
capsize=4, c='C1', fmt='o')
3432+
ax_ref.plot(x[1::3], y[1::3] + 0.2, 'o', c='C1', zorder=2.1)
3433+
ax_ref.errorbar(x[1::3], y[1::3] + 0.2, yerr[1::3],
3434+
capsize=4, c='C1', fmt='none')
3435+
33983436

33993437
@image_comparison(['hist_stacked_stepfilled', 'hist_stacked_stepfilled'])
34003438
def test_hist_stacked_stepfilled():

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy