@@ -3199,10 +3199,13 @@ def errorbar(self, x, y, yerr=None, xerr=None,
31993199
32003200 lolims, uplims, xlolims, xuplims : bool, default: False
32013201 These arguments can be used to indicate that a value gives only
3202- upper/lower limits. In that case a caret symbol is used to
3203- indicate this. *lims*-arguments may be of the same type as *xerr*
3204- and *yerr*. To use limits with inverted axes, `~.Axes.set_xlim`
3205- or `~.Axes.set_ylim` must be called before :meth:`errorbar`.
3202+ upper/lower limits. In that case a caret symbol is used to
3203+ indicate this. *lims*-arguments may be scalars, or array-likes of
3204+ the same length as *xerr* and *yerr*. To use limits with inverted
3205+ axes, `~.Axes.set_xlim` or `~.Axes.set_ylim` must be called before
3206+ :meth:`errorbar`. Note the tricky parameter names: setting e.g.
3207+ *lolims* to True means that the y-value is a *lower* limit of the
3208+ True value, so, only an *upward*-pointing arrow will be drawn!
32063209
32073210 errorevery : int or (int, int), default: 1
32083211 draws error bars on a subset of the data. *errorevery* =N draws
@@ -3296,6 +3299,9 @@ def errorbar(self, x, y, yerr=None, xerr=None,
32963299 if not np .iterable (y ):
32973300 y = [y ]
32983301
3302+ if len (x ) != len (y ):
3303+ raise ValueError ("'x' and 'y' must have the same size" )
3304+
32993305 if xerr is not None :
33003306 if not np .iterable (xerr ):
33013307 xerr = [xerr ] * len (x )
@@ -3368,22 +3374,29 @@ def errorbar(self, x, y, yerr=None, xerr=None,
33683374 everymask = np .zeros (len (x ), bool )
33693375 everymask [offset ::errorevery ] = True
33703376
3371- def xywhere (xs , ys , mask ):
3372- """
3373- Return xs[mask], ys[mask] where mask is True but xs and
3374- ys are not arrays.
3375- """
3376- assert len (xs ) == len (ys )
3377- assert len (xs ) == len (mask )
3378- xs = [thisx for thisx , b in zip (xs , mask ) if b ]
3379- ys = [thisy for thisy , b in zip (ys , mask ) if b ]
3380- return xs , ys
3377+ def apply_mask (arrays , mask ):
3378+ # Return, for each array in *arrays*, the elements for which *mask*
3379+ # is True, without using fancy indexing.
3380+ return [[* itertools .compress (array , mask )] for array in arrays ]
33813381
3382- def extract_err (name , err , data ):
3382+ def extract_err (name , err , data , lolims , uplims ):
33833383 """
3384- Private function to parse *err* and subtract/add it to *data*.
3385-
3386- Both *err* and *data* are already iterables at this point.
3384+ Private function to compute error bars.
3385+
3386+ Parameters
3387+ ----------
3388+ name : {'x', 'y'}
3389+ Name used in the error message.
3390+ err : array-like
3391+ xerr or yerr from errorbar().
3392+ data : array-like
3393+ x or y from errorbar().
3394+ lolims : array-like
3395+ Error is only applied on **upper** side when this is True. See
3396+ the note in the main docstring about this parameter's name.
3397+ uplims : array-like
3398+ Error is only applied on **lower** side when this is True. See
3399+ the note in the main docstring about this parameter's name.
33873400 """
33883401 try : # Asymmetric error: pair of 1D iterables.
33893402 a , b = err
@@ -3400,116 +3413,92 @@ def extract_err(name, err, data):
34003413 raise ValueError (
34013414 f"The lengths of the data ({ len (data )} ) and the "
34023415 f"error { len (e )} do not match" )
3403- low = [v - e for v , e in zip (data , a )]
3404- high = [v + e for v , e in zip (data , b )]
3416+ low = [v if lo else v - e for v , e , lo in zip (data , a , lolims )]
3417+ high = [v if up else v + e for v , e , up in zip (data , b , uplims )]
34053418 return low , high
34063419
34073420 if xerr is not None :
3408- left , right = extract_err ('x' , xerr , x )
3421+ left , right = extract_err ('x' , xerr , x , xlolims , xuplims )
3422+ barcols .append (self .hlines (
3423+ * apply_mask ([y , left , right ], everymask ), ** eb_lines_style ))
34093424 # select points without upper/lower limits in x and
34103425 # draw normal errorbars for these points
34113426 noxlims = ~ (xlolims | xuplims )
3412- if noxlims .any () or len (noxlims ) == 0 :
3413- yo , _ = xywhere (y , right , noxlims & everymask )
3414- lo , ro = xywhere (left , right , noxlims & everymask )
3415- barcols .append (self .hlines (yo , lo , ro , ** eb_lines_style ))
3416- if capsize > 0 :
3417- caplines .append (mlines .Line2D (lo , yo , marker = '|' ,
3418- ** eb_cap_style ))
3419- caplines .append (mlines .Line2D (ro , yo , marker = '|' ,
3420- ** eb_cap_style ))
3421-
3427+ if noxlims .any () and capsize > 0 :
3428+ yo , lo , ro = apply_mask ([y , left , right ], noxlims & everymask )
3429+ caplines .extend ([
3430+ mlines .Line2D (lo , yo , marker = '|' , ** eb_cap_style ),
3431+ mlines .Line2D (ro , yo , marker = '|' , ** eb_cap_style )])
34223432 if xlolims .any ():
3423- yo , _ = xywhere (y , right , xlolims & everymask )
3424- lo , ro = xywhere (x , right , xlolims & everymask )
3425- barcols .append (self .hlines (yo , lo , ro , ** eb_lines_style ))
3426- rightup , yup = xywhere (right , y , xlolims & everymask )
3433+ xo , yo , lo , ro = apply_mask ([x , y , left , right ],
3434+ xlolims & everymask )
34273435 if self .xaxis_inverted ():
34283436 marker = mlines .CARETLEFTBASE
34293437 else :
34303438 marker = mlines .CARETRIGHTBASE
3431- caplines .append (
3432- mlines .Line2D (rightup , yup , ls = 'None' , marker = marker ,
3433- ** eb_cap_style ))
3439+ caplines .append (mlines .Line2D (
3440+ ro , yo , ls = 'None' , marker = marker , ** eb_cap_style ))
34343441 if capsize > 0 :
3435- xlo , ylo = xywhere (x , y , xlolims & everymask )
3436- caplines .append (mlines .Line2D (xlo , ylo , marker = '|' ,
3437- ** eb_cap_style ))
3438-
3442+ caplines .append (mlines .Line2D (
3443+ xo , yo , marker = '|' , ** eb_cap_style ))
34393444 if xuplims .any ():
3440- yo , _ = xywhere (y , right , xuplims & everymask )
3441- lo , ro = xywhere (left , x , xuplims & everymask )
3442- barcols .append (self .hlines (yo , lo , ro , ** eb_lines_style ))
3443- leftlo , ylo = xywhere (left , y , xuplims & everymask )
3445+ xo , yo , lo , ro = apply_mask ([x , y , left , right ],
3446+ xuplims & everymask )
34443447 if self .xaxis_inverted ():
34453448 marker = mlines .CARETRIGHTBASE
34463449 else :
34473450 marker = mlines .CARETLEFTBASE
3448- caplines .append (
3449- mlines .Line2D (leftlo , ylo , ls = 'None' , marker = marker ,
3450- ** eb_cap_style ))
3451+ caplines .append (mlines .Line2D (
3452+ lo , yo , ls = 'None' , marker = marker , ** eb_cap_style ))
34513453 if capsize > 0 :
3452- xup , yup = xywhere (x , y , xuplims & everymask )
3453- caplines .append (mlines .Line2D (xup , yup , marker = '|' ,
3454- ** eb_cap_style ))
3454+ caplines .append (mlines .Line2D (
3455+ xo , yo , marker = '|' , ** eb_cap_style ))
34553456
34563457 if yerr is not None :
3457- lower , upper = extract_err ('y' , yerr , y )
3458+ lower , upper = extract_err ('y' , yerr , y , lolims , uplims )
3459+ barcols .append (self .vlines (
3460+ * apply_mask ([x , lower , upper ], everymask ), ** eb_lines_style ))
34583461 # select points without upper/lower limits in y and
34593462 # draw normal errorbars for these points
34603463 noylims = ~ (lolims | uplims )
3461- if noylims .any () or len (noylims ) == 0 :
3462- xo , _ = xywhere (x , lower , noylims & everymask )
3463- lo , uo = xywhere (lower , upper , noylims & everymask )
3464- barcols .append (self .vlines (xo , lo , uo , ** eb_lines_style ))
3465- if capsize > 0 :
3466- caplines .append (mlines .Line2D (xo , lo , marker = '_' ,
3467- ** eb_cap_style ))
3468- caplines .append (mlines .Line2D (xo , uo , marker = '_' ,
3469- ** eb_cap_style ))
3470-
3464+ if noylims .any () and capsize > 0 :
3465+ xo , lo , uo = apply_mask ([x , lower , upper ], noylims & everymask )
3466+ caplines .extend ([
3467+ mlines .Line2D (xo , lo , marker = '_' , ** eb_cap_style ),
3468+ mlines .Line2D (xo , uo , marker = '_' , ** eb_cap_style )])
34713469 if lolims .any ():
3472- xo , _ = xywhere (x , lower , lolims & everymask )
3473- lo , uo = xywhere (y , upper , lolims & everymask )
3474- barcols .append (self .vlines (xo , lo , uo , ** eb_lines_style ))
3475- xup , upperup = xywhere (x , upper , lolims & everymask )
3470+ xo , yo , lo , uo = apply_mask ([x , y , lower , upper ],
3471+ lolims & everymask )
34763472 if self .yaxis_inverted ():
34773473 marker = mlines .CARETDOWNBASE
34783474 else :
34793475 marker = mlines .CARETUPBASE
3480- caplines .append (
3481- mlines .Line2D (xup , upperup , ls = 'None' , marker = marker ,
3482- ** eb_cap_style ))
3476+ caplines .append (mlines .Line2D (
3477+ xo , uo , ls = 'None' , marker = marker , ** eb_cap_style ))
34833478 if capsize > 0 :
3484- xlo , ylo = xywhere (x , y , lolims & everymask )
3485- caplines .append (mlines .Line2D (xlo , ylo , marker = '_' ,
3486- ** eb_cap_style ))
3487-
3479+ caplines .append (mlines .Line2D (
3480+ xo , yo , marker = '_' , ** eb_cap_style ))
34883481 if uplims .any ():
3489- xo , _ = xywhere (x , lower , uplims & everymask )
3490- lo , uo = xywhere (lower , y , uplims & everymask )
3491- barcols .append (self .vlines (xo , lo , uo , ** eb_lines_style ))
3492- xlo , lowerlo = xywhere (x , lower , uplims & everymask )
3482+ xo , yo , lo , uo = apply_mask ([x , y , lower , upper ],
3483+ uplims & everymask )
34933484 if self .yaxis_inverted ():
34943485 marker = mlines .CARETUPBASE
34953486 else :
34963487 marker = mlines .CARETDOWNBASE
3497- caplines .append (
3498- mlines .Line2D (xlo , lowerlo , ls = 'None' , marker = marker ,
3499- ** eb_cap_style ))
3488+ caplines .append (mlines .Line2D (
3489+ xo , lo , ls = 'None' , marker = marker , ** eb_cap_style ))
35003490 if capsize > 0 :
3501- xup , yup = xywhere ( x , y , uplims & everymask )
3502- caplines . append ( mlines . Line2D ( xup , yup , marker = '_' ,
3503- ** eb_cap_style ))
3491+ caplines . append ( mlines . Line2D (
3492+ xo , yo , marker = '_' , ** eb_cap_style ))
3493+
35043494 for l in caplines :
35053495 self .add_line (l )
35063496
35073497 self ._request_autoscale_view ()
3508- errorbar_container = ErrorbarContainer ((data_line , tuple (caplines ),
3509- tuple (barcols )),
3510- has_xerr = (xerr is not None ),
3511- has_yerr = (yerr is not None ),
3512- label = label )
3498+ errorbar_container = ErrorbarContainer (
3499+ (data_line , tuple (caplines ), tuple (barcols )),
3500+ has_xerr = (xerr is not None ), has_yerr = (yerr is not None ),
3501+ label = label )
35133502 self .containers .append (errorbar_container )
35143503
35153504 return errorbar_container # (l0, caplines, barcols)
0 commit comments