@@ -3200,10 +3200,13 @@ def errorbar(self, x, y, yerr=None, xerr=None,
3200
3200
3201
3201
lolims, uplims, xlolims, xuplims : bool, default: False
3202
3202
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!
3207
3210
3208
3211
errorevery : int or (int, int), default: 1
3209
3212
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,
3297
3300
if not np .iterable (y ):
3298
3301
y = [y ]
3299
3302
3303
+ if len (x ) != len (y ):
3304
+ raise ValueError ("'x' and 'y' must have the same size" )
3305
+
3300
3306
if xerr is not None :
3301
3307
if not np .iterable (xerr ):
3302
3308
xerr = [xerr ] * len (x )
@@ -3369,22 +3375,29 @@ def errorbar(self, x, y, yerr=None, xerr=None,
3369
3375
everymask = np .zeros (len (x ), bool )
3370
3376
everymask [offset ::errorevery ] = True
3371
3377
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 ]
3382
3382
3383
- def extract_err (name , err , data ):
3383
+ def extract_err (name , err , data , lolims , uplims ):
3384
3384
"""
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.
3388
3401
"""
3389
3402
try : # Asymmetric error: pair of 1D iterables.
3390
3403
a , b = err
@@ -3401,116 +3414,92 @@ def extract_err(name, err, data):
3401
3414
raise ValueError (
3402
3415
f"The lengths of the data ({ len (data )} ) and the "
3403
3416
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 )]
3406
3419
return low , high
3407
3420
3408
3421
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 ))
3410
3425
# select points without upper/lower limits in x and
3411
3426
# draw normal errorbars for these points
3412
3427
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 )])
3423
3433
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 )
3428
3436
if self .xaxis_inverted ():
3429
3437
marker = mlines .CARETLEFTBASE
3430
3438
else :
3431
3439
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 ))
3435
3442
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 ))
3440
3445
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 )
3445
3448
if self .xaxis_inverted ():
3446
3449
marker = mlines .CARETRIGHTBASE
3447
3450
else :
3448
3451
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 ))
3452
3454
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 ))
3456
3457
3457
3458
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 ))
3459
3462
# select points without upper/lower limits in y and
3460
3463
# draw normal errorbars for these points
3461
3464
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 )])
3472
3470
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 )
3477
3473
if self .yaxis_inverted ():
3478
3474
marker = mlines .CARETDOWNBASE
3479
3475
else :
3480
3476
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 ))
3484
3479
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 ))
3489
3482
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 )
3494
3485
if self .yaxis_inverted ():
3495
3486
marker = mlines .CARETUPBASE
3496
3487
else :
3497
3488
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 ))
3501
3491
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
+
3505
3495
for l in caplines :
3506
3496
self .add_line (l )
3507
3497
3508
3498
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 )
3514
3503
self .containers .append (errorbar_container )
3515
3504
3516
3505
return errorbar_container # (l0, caplines, barcols)
0 commit comments