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

Skip to content

Commit c24b1af

Browse files
committed
Simplifications to errorbar().
- Move check that x and y have same size to top. - Replace xywhere by a more general mask-application function; then, in the various xlolims/xuplims/... handling blocks, apply all masks at once. - Apply xlolims/xuplims in extract_err, which allows drawing all errorbars in a single call to vlines()/hlines(). - We don't need to check the size of lolims/uplims because they go through broadcast() earlier. - Clarify the docstring a bit.
1 parent 5ab1352 commit c24b1af

File tree

1 file changed

+78
-89
lines changed

1 file changed

+78
-89
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 78 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -3200,10 +3200,13 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32003200
32013201
lolims, uplims, xlolims, xuplims : bool, default: False
32023202
These arguments can be used to indicate that a value gives only
3203-
upper/lower limits. In that case a caret symbol is used to
3204-
indicate this. *lims*-arguments may be of the same type as *xerr*
3205-
and *yerr*. To use limits with inverted axes, `~.Axes.set_xlim`
3206-
or `~.Axes.set_ylim` must be called before :meth:`errorbar`.
3203+
upper/lower limits. In that case a caret symbol is used to
3204+
indicate this. *lims*-arguments may be scalars, or array-likes of
3205+
the same length as *xerr* and *yerr*. To use limits with inverted
3206+
axes, `~.Axes.set_xlim` or `~.Axes.set_ylim` must be called before
3207+
:meth:`errorbar`. Note the tricky parameter names: setting e.g.
3208+
*lolims* to True means that the y-value is a *lower* limit of the
3209+
True value, so, only an *upward*-pointing arrow will be drawn!
32073210
32083211
errorevery : int or (int, int), default: 1
32093212
draws error bars on a subset of the data. *errorevery* =N draws
@@ -3297,6 +3300,9 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32973300
if not np.iterable(y):
32983301
y = [y]
32993302

3303+
if len(x) != len(y):
3304+
raise ValueError("'x' and 'y' must have the same size")
3305+
33003306
if xerr is not None:
33013307
if not np.iterable(xerr):
33023308
xerr = [xerr] * len(x)
@@ -3369,22 +3375,29 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33693375
everymask = np.zeros(len(x), bool)
33703376
everymask[offset::errorevery] = True
33713377

3372-
def xywhere(xs, ys, mask):
3373-
"""
3374-
Return xs[mask], ys[mask] where mask is True but xs and
3375-
ys are not arrays.
3376-
"""
3377-
assert len(xs) == len(ys)
3378-
assert len(xs) == len(mask)
3379-
xs = [thisx for thisx, b in zip(xs, mask) if b]
3380-
ys = [thisy for thisy, b in zip(ys, mask) if b]
3381-
return xs, ys
3378+
def apply_mask(arrays, mask):
3379+
# Return, for each array in *arrays*, the elements for which *mask*
3380+
# is True, without using fancy indexing.
3381+
return [[*itertools.compress(array, mask)] for array in arrays]
33823382

3383-
def extract_err(name, err, data):
3383+
def extract_err(name, err, data, lolims, uplims):
33843384
"""
3385-
Private function to parse *err* and subtract/add it to *data*.
3386-
3387-
Both *err* and *data* are already iterables at this point.
3385+
Private function to compute error bars.
3386+
3387+
Parameters
3388+
----------
3389+
name : {'x', 'y'}
3390+
Name used in the error message.
3391+
err : array-like
3392+
xerr or yerr from errorbar().
3393+
data : array-like
3394+
x or y from errorbar().
3395+
lolims : array-like
3396+
Error is only applied on **upper** side when this is True. See
3397+
the note in the main docstring about this parameter's name.
3398+
uplims : array-like
3399+
Error is only applied on **lower** side when this is True. See
3400+
the note in the main docstring about this parameter's name.
33883401
"""
33893402
try: # Asymmetric error: pair of 1D iterables.
33903403
a, b = err
@@ -3401,116 +3414,92 @@ def extract_err(name, err, data):
34013414
raise ValueError(
34023415
f"The lengths of the data ({len(data)}) and the "
34033416
f"error {len(e)} do not match")
3404-
low = [v - e for v, e in zip(data, a)]
3405-
high = [v + e for v, e in zip(data, b)]
3417+
low = [v if lo else v - e for v, e, lo in zip(data, a, lolims)]
3418+
high = [v if up else v + e for v, e, up in zip(data, b, uplims)]
34063419
return low, high
34073420

34083421
if xerr is not None:
3409-
left, right = extract_err('x', xerr, x)
3422+
left, right = extract_err('x', xerr, x, xlolims, xuplims)
3423+
barcols.append(self.hlines(
3424+
*apply_mask([y, left, right], everymask), **eb_lines_style))
34103425
# select points without upper/lower limits in x and
34113426
# draw normal errorbars for these points
34123427
noxlims = ~(xlolims | xuplims)
3413-
if noxlims.any() or len(noxlims) == 0:
3414-
yo, _ = xywhere(y, right, noxlims & everymask)
3415-
lo, ro = xywhere(left, right, noxlims & everymask)
3416-
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
3417-
if capsize > 0:
3418-
caplines.append(mlines.Line2D(lo, yo, marker='|',
3419-
**eb_cap_style))
3420-
caplines.append(mlines.Line2D(ro, yo, marker='|',
3421-
**eb_cap_style))
3422-
3428+
if noxlims.any() and capsize > 0:
3429+
yo, lo, ro = apply_mask([y, left, right], noxlims & everymask)
3430+
caplines.extend([
3431+
mlines.Line2D(lo, yo, marker='|', **eb_cap_style),
3432+
mlines.Line2D(ro, yo, marker='|', **eb_cap_style)])
34233433
if xlolims.any():
3424-
yo, _ = xywhere(y, right, xlolims & everymask)
3425-
lo, ro = xywhere(x, right, xlolims & everymask)
3426-
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
3427-
rightup, yup = xywhere(right, y, xlolims & everymask)
3434+
xo, yo, lo, ro = apply_mask([x, y, left, right],
3435+
xlolims & everymask)
34283436
if self.xaxis_inverted():
34293437
marker = mlines.CARETLEFTBASE
34303438
else:
34313439
marker = mlines.CARETRIGHTBASE
3432-
caplines.append(
3433-
mlines.Line2D(rightup, yup, ls='None', marker=marker,
3434-
**eb_cap_style))
3440+
caplines.append(mlines.Line2D(
3441+
ro, yo, ls='None', marker=marker, **eb_cap_style))
34353442
if capsize > 0:
3436-
xlo, ylo = xywhere(x, y, xlolims & everymask)
3437-
caplines.append(mlines.Line2D(xlo, ylo, marker='|',
3438-
**eb_cap_style))
3439-
3443+
caplines.append(mlines.Line2D(
3444+
xo, yo, marker='|', **eb_cap_style))
34403445
if xuplims.any():
3441-
yo, _ = xywhere(y, right, xuplims & everymask)
3442-
lo, ro = xywhere(left, x, xuplims & everymask)
3443-
barcols.append(self.hlines(yo, lo, ro, **eb_lines_style))
3444-
leftlo, ylo = xywhere(left, y, xuplims & everymask)
3446+
xo, yo, lo, ro = apply_mask([x, y, left, right],
3447+
xuplims & everymask)
34453448
if self.xaxis_inverted():
34463449
marker = mlines.CARETRIGHTBASE
34473450
else:
34483451
marker = mlines.CARETLEFTBASE
3449-
caplines.append(
3450-
mlines.Line2D(leftlo, ylo, ls='None', marker=marker,
3451-
**eb_cap_style))
3452+
caplines.append(mlines.Line2D(
3453+
lo, yo, ls='None', marker=marker, **eb_cap_style))
34523454
if capsize > 0:
3453-
xup, yup = xywhere(x, y, xuplims & everymask)
3454-
caplines.append(mlines.Line2D(xup, yup, marker='|',
3455-
**eb_cap_style))
3455+
caplines.append(mlines.Line2D(
3456+
xo, yo, marker='|', **eb_cap_style))
34563457

34573458
if yerr is not None:
3458-
lower, upper = extract_err('y', yerr, y)
3459+
lower, upper = extract_err('y', yerr, y, lolims, uplims)
3460+
barcols.append(self.vlines(
3461+
*apply_mask([x, lower, upper], everymask), **eb_lines_style))
34593462
# select points without upper/lower limits in y and
34603463
# draw normal errorbars for these points
34613464
noylims = ~(lolims | uplims)
3462-
if noylims.any() or len(noylims) == 0:
3463-
xo, _ = xywhere(x, lower, noylims & everymask)
3464-
lo, uo = xywhere(lower, upper, noylims & everymask)
3465-
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
3466-
if capsize > 0:
3467-
caplines.append(mlines.Line2D(xo, lo, marker='_',
3468-
**eb_cap_style))
3469-
caplines.append(mlines.Line2D(xo, uo, marker='_',
3470-
**eb_cap_style))
3471-
3465+
if noylims.any() and capsize > 0:
3466+
xo, lo, uo = apply_mask([x, lower, upper], noylims & everymask)
3467+
caplines.extend([
3468+
mlines.Line2D(xo, lo, marker='_', **eb_cap_style),
3469+
mlines.Line2D(xo, uo, marker='_', **eb_cap_style)])
34723470
if lolims.any():
3473-
xo, _ = xywhere(x, lower, lolims & everymask)
3474-
lo, uo = xywhere(y, upper, lolims & everymask)
3475-
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
3476-
xup, upperup = xywhere(x, upper, lolims & everymask)
3471+
xo, yo, lo, uo = apply_mask([x, y, lower, upper],
3472+
lolims & everymask)
34773473
if self.yaxis_inverted():
34783474
marker = mlines.CARETDOWNBASE
34793475
else:
34803476
marker = mlines.CARETUPBASE
3481-
caplines.append(
3482-
mlines.Line2D(xup, upperup, ls='None', marker=marker,
3483-
**eb_cap_style))
3477+
caplines.append(mlines.Line2D(
3478+
xo, uo, ls='None', marker=marker, **eb_cap_style))
34843479
if capsize > 0:
3485-
xlo, ylo = xywhere(x, y, lolims & everymask)
3486-
caplines.append(mlines.Line2D(xlo, ylo, marker='_',
3487-
**eb_cap_style))
3488-
3480+
caplines.append(mlines.Line2D(
3481+
xo, yo, marker='_', **eb_cap_style))
34893482
if uplims.any():
3490-
xo, _ = xywhere(x, lower, uplims & everymask)
3491-
lo, uo = xywhere(lower, y, uplims & everymask)
3492-
barcols.append(self.vlines(xo, lo, uo, **eb_lines_style))
3493-
xlo, lowerlo = xywhere(x, lower, uplims & everymask)
3483+
xo, yo, lo, uo = apply_mask([x, y, lower, upper],
3484+
uplims & everymask)
34943485
if self.yaxis_inverted():
34953486
marker = mlines.CARETUPBASE
34963487
else:
34973488
marker = mlines.CARETDOWNBASE
3498-
caplines.append(
3499-
mlines.Line2D(xlo, lowerlo, ls='None', marker=marker,
3500-
**eb_cap_style))
3489+
caplines.append(mlines.Line2D(
3490+
xo, lo, ls='None', marker=marker, **eb_cap_style))
35013491
if capsize > 0:
3502-
xup, yup = xywhere(x, y, uplims & everymask)
3503-
caplines.append(mlines.Line2D(xup, yup, marker='_',
3504-
**eb_cap_style))
3492+
caplines.append(mlines.Line2D(
3493+
xo, yo, marker='_', **eb_cap_style))
3494+
35053495
for l in caplines:
35063496
self.add_line(l)
35073497

35083498
self._request_autoscale_view()
3509-
errorbar_container = ErrorbarContainer((data_line, tuple(caplines),
3510-
tuple(barcols)),
3511-
has_xerr=(xerr is not None),
3512-
has_yerr=(yerr is not None),
3513-
label=label)
3499+
errorbar_container = ErrorbarContainer(
3500+
(data_line, tuple(caplines), tuple(barcols)),
3501+
has_xerr=(xerr is not None), has_yerr=(yerr is not None),
3502+
label=label)
35143503
self.containers.append(errorbar_container)
35153504

35163505
return errorbar_container # (l0, caplines, barcols)

0 commit comments

Comments
 (0)