Skip to content

Commit 9113e53

Browse files
committed
Factor out x/y lo/hi handling in errorbar.
1 parent 183018c commit 9113e53

File tree

1 file changed

+50
-100
lines changed

1 file changed

+50
-100
lines changed

lib/matplotlib/axes/_axes.py

+50-100
Original file line numberDiff line numberDiff line change
@@ -3398,116 +3398,66 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33983398
barcols = []
33993399
caplines = []
34003400

3401-
# arrays fine here, they are booleans and hence not units
3402-
lolims = np.broadcast_to(lolims, len(x)).astype(bool)
3403-
uplims = np.broadcast_to(uplims, len(x)).astype(bool)
3404-
xlolims = np.broadcast_to(xlolims, len(x)).astype(bool)
3405-
xuplims = np.broadcast_to(xuplims, len(x)).astype(bool)
3406-
34073401
# Vectorized fancy-indexer.
34083402
def apply_mask(arrays, mask): return [array[mask] for array in arrays]
34093403

3410-
def extract_err(name, err, data, lolims, uplims):
3411-
"""
3412-
Private function to compute error bars.
3413-
3414-
Parameters
3415-
----------
3416-
name : {'x', 'y'}
3417-
Name used in the error message.
3418-
err : array-like
3419-
xerr or yerr from errorbar().
3420-
data : array-like
3421-
x or y from errorbar().
3422-
lolims : array-like
3423-
Error is only applied on **upper** side when this is True. See
3424-
the note in the main docstring about this parameter's name.
3425-
uplims : array-like
3426-
Error is only applied on **lower** side when this is True. See
3427-
the note in the main docstring about this parameter's name.
3428-
"""
3404+
# dep: dependent dataset, indep: independent dataset
3405+
for (dep_axis, dep, err, lolims, uplims, indep, lines_func,
3406+
marker, lomarker, himarker) in [
3407+
("x", x, xerr, xlolims, xuplims, y, self.hlines,
3408+
"|", mlines.CARETRIGHTBASE, mlines.CARETLEFTBASE),
3409+
("y", y, yerr, lolims, uplims, x, self.vlines,
3410+
"_", mlines.CARETUPBASE, mlines.CARETDOWNBASE),
3411+
]:
3412+
if err is None:
3413+
continue
3414+
lolims = np.broadcast_to(lolims, len(dep)).astype(bool)
3415+
uplims = np.broadcast_to(uplims, len(dep)).astype(bool)
34293416
try:
3430-
np.broadcast_to(err, (2, len(data)))
3417+
np.broadcast_to(err, (2, len(dep)))
34313418
except ValueError:
34323419
raise ValueError(
3433-
f"'{name}err' (shape: {np.shape(err)}) must be a scalar "
3434-
f"or a 1D or (2, n) array-like whose shape matches "
3435-
f"'{name}' (shape: {np.shape(data)})") from None
3420+
f"'{dep_axis}err' (shape: {np.shape(err)}) must be a "
3421+
f"scalar or a 1D or (2, n) array-like whose shape matches "
3422+
f"'{dep_axis}' (shape: {np.shape(dep)})") from None
34363423
# This is like
3437-
# low, high = np.broadcast_to(...)
3438-
# return data - low * ~lolims, data + high * ~uplims
3424+
# elow, ehigh = np.broadcast_to(...)
3425+
# return dep - elow * ~lolims, dep + ehigh * ~uplims
34393426
# except that broadcast_to would strip units.
3440-
return data + np.row_stack([-(1 - lolims), 1 - uplims]) * err
3441-
3442-
if xerr is not None:
3443-
left, right = extract_err('x', xerr, x, xlolims, xuplims)
3444-
barcols.append(self.hlines(
3445-
*apply_mask([y, left, right], everymask), **eb_lines_style))
3446-
# select points without upper/lower limits in x and
3447-
# draw normal errorbars for these points
3448-
noxlims = ~(xlolims | xuplims)
3449-
if noxlims.any() and capsize > 0:
3450-
yo, lo, ro = apply_mask([y, left, right], noxlims & everymask)
3451-
caplines.extend([
3452-
mlines.Line2D(lo, yo, marker='|', **eb_cap_style),
3453-
mlines.Line2D(ro, yo, marker='|', **eb_cap_style)])
3454-
if xlolims.any():
3455-
xo, yo, ro = apply_mask([x, y, right], xlolims & everymask)
3456-
if self.xaxis_inverted():
3457-
marker = mlines.CARETLEFTBASE
3458-
else:
3459-
marker = mlines.CARETRIGHTBASE
3460-
caplines.append(mlines.Line2D(
3461-
ro, yo, ls='None', marker=marker, **eb_cap_style))
3462-
if capsize > 0:
3463-
caplines.append(mlines.Line2D(
3464-
xo, yo, marker='|', **eb_cap_style))
3465-
if xuplims.any():
3466-
xo, yo, lo = apply_mask([x, y, left], xuplims & everymask)
3467-
if self.xaxis_inverted():
3468-
marker = mlines.CARETRIGHTBASE
3469-
else:
3470-
marker = mlines.CARETLEFTBASE
3471-
caplines.append(mlines.Line2D(
3472-
lo, yo, ls='None', marker=marker, **eb_cap_style))
3473-
if capsize > 0:
3474-
caplines.append(mlines.Line2D(
3475-
xo, yo, marker='|', **eb_cap_style))
3476-
3477-
if yerr is not None:
3478-
lower, upper = extract_err('y', yerr, y, lolims, uplims)
3479-
barcols.append(self.vlines(
3480-
*apply_mask([x, lower, upper], everymask), **eb_lines_style))
3481-
# select points without upper/lower limits in y and
3482-
# draw normal errorbars for these points
3483-
noylims = ~(lolims | uplims)
3484-
if noylims.any() and capsize > 0:
3485-
xo, lo, uo = apply_mask([x, lower, upper], noylims & everymask)
3486-
caplines.extend([
3487-
mlines.Line2D(xo, lo, marker='_', **eb_cap_style),
3488-
mlines.Line2D(xo, uo, marker='_', **eb_cap_style)])
3489-
if lolims.any():
3490-
xo, yo, uo = apply_mask([x, y, upper], lolims & everymask)
3491-
if self.yaxis_inverted():
3492-
marker = mlines.CARETDOWNBASE
3493-
else:
3494-
marker = mlines.CARETUPBASE
3495-
caplines.append(mlines.Line2D(
3496-
xo, uo, ls='None', marker=marker, **eb_cap_style))
3497-
if capsize > 0:
3498-
caplines.append(mlines.Line2D(
3499-
xo, yo, marker='_', **eb_cap_style))
3500-
if uplims.any():
3501-
xo, yo, lo = apply_mask([x, y, lower], uplims & everymask)
3502-
if self.yaxis_inverted():
3503-
marker = mlines.CARETUPBASE
3504-
else:
3505-
marker = mlines.CARETDOWNBASE
3506-
caplines.append(mlines.Line2D(
3507-
xo, lo, ls='None', marker=marker, **eb_cap_style))
3427+
low, high = dep + np.row_stack([-(1 - lolims), 1 - uplims]) * err
3428+
3429+
barcols.append(lines_func(
3430+
*apply_mask([indep, low, high], everymask), **eb_lines_style))
3431+
# Normal errorbars for points without upper/lower limits.
3432+
nolims = ~(lolims | uplims)
3433+
if nolims.any() and capsize > 0:
3434+
indep_masked, lo_masked, hi_masked = apply_mask(
3435+
[indep, low, high], nolims & everymask)
3436+
for lh_masked in [lo_masked, hi_masked]:
3437+
# Since this has to work for x and y as dependent data, we
3438+
# first set both x and y to the independent variable and
3439+
# overwrite the respective dependent data in a second step.
3440+
line = mlines.Line2D(indep_masked, indep_masked,
3441+
marker=marker, **eb_cap_style)
3442+
line.set(**{f"{dep_axis}data": lh_masked})
3443+
caplines.append(line)
3444+
for idx, (lims, hl) in enumerate([(lolims, high), (uplims, low)]):
3445+
if not lims.any():
3446+
continue
3447+
hlmarker = (
3448+
himarker
3449+
if getattr(self, f"{dep_axis}axis").get_inverted() ^ idx
3450+
else lomarker)
3451+
x_masked, y_masked, hl_masked = apply_mask(
3452+
[x, y, hl], lims & everymask)
3453+
# As above, we set the dependent data in a second step.
3454+
line = mlines.Line2D(x_masked, y_masked,
3455+
marker=hlmarker, **eb_cap_style)
3456+
line.set(**{f"{dep_axis}data": hl_masked})
3457+
caplines.append(line)
35083458
if capsize > 0:
35093459
caplines.append(mlines.Line2D(
3510-
xo, yo, marker='_', **eb_cap_style))
3460+
x_masked, y_masked, marker=marker, **eb_cap_style))
35113461

35123462
for l in caplines:
35133463
self.add_line(l)

0 commit comments

Comments
 (0)