Skip to content

Commit 31d5636

Browse files
authored
Merge pull request #19869 from anntzer/errorbarxy
Factor out x/y lo/hi handling in errorbar.
2 parents ab07898 + 9113e53 commit 31d5636

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
@@ -3409,116 +3409,66 @@ def errorbar(self, x, y, yerr=None, xerr=None,
34093409
barcols = []
34103410
caplines = []
34113411

3412-
# arrays fine here, they are booleans and hence not units
3413-
lolims = np.broadcast_to(lolims, len(x)).astype(bool)
3414-
uplims = np.broadcast_to(uplims, len(x)).astype(bool)
3415-
xlolims = np.broadcast_to(xlolims, len(x)).astype(bool)
3416-
xuplims = np.broadcast_to(xuplims, len(x)).astype(bool)
3417-
34183412
# Vectorized fancy-indexer.
34193413
def apply_mask(arrays, mask): return [array[mask] for array in arrays]
34203414

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

35233473
for l in caplines:
35243474
self.add_line(l)

0 commit comments

Comments
 (0)