@@ -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,27 @@ 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.
3397
+ uplims : array-like
3398
+ Error is only applied on lower side when this is True.
3388
3399
"""
3389
3400
try : # Asymmetric error: pair of 1D iterables.
3390
3401
a , b = err
@@ -3401,116 +3412,92 @@ def extract_err(name, err, data):
3401
3412
raise ValueError (
3402
3413
f"The lengths of the data ({ len (data )} ) and the "
3403
3414
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 )]
3415
+ low = [v if lo else v - e for v , e , lo in zip (data , a , lolims )]
3416
+ high = [v if up else v + e for v , e , up in zip (data , b , uplims )]
3406
3417
return low , high
3407
3418
3408
3419
if xerr is not None :
3409
- left , right = extract_err ('x' , xerr , x )
3420
+ left , right = extract_err ('x' , xerr , x , xlolims , xuplims )
3421
+ barcols .append (self .hlines (
3422
+ * apply_mask ([y , left , right ], everymask ), ** eb_lines_style ))
3410
3423
# select points without upper/lower limits in x and
3411
3424
# draw normal errorbars for these points
3412
3425
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
-
3426
+ if noxlims .any () and capsize > 0 :
3427
+ yo , lo , ro = apply_mask ([y , left , right ], noxlims & everymask )
3428
+ caplines .extend ([
3429
+ mlines .Line2D (lo , yo , marker = '|' , ** eb_cap_style ),
3430
+ mlines .Line2D (ro , yo , marker = '|' , ** eb_cap_style )])
3423
3431
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 )
3432
+ xo , yo , lo , ro = apply_mask ([x , y , left , right ],
3433
+ xlolims & everymask )
3428
3434
if self .xaxis_inverted ():
3429
3435
marker = mlines .CARETLEFTBASE
3430
3436
else :
3431
3437
marker = mlines .CARETRIGHTBASE
3432
- caplines .append (
3433
- mlines .Line2D (rightup , yup , ls = 'None' , marker = marker ,
3434
- ** eb_cap_style ))
3438
+ caplines .append (mlines .Line2D (
3439
+ ro , yo , ls = 'None' , marker = marker , ** eb_cap_style ))
3435
3440
if capsize > 0 :
3436
- xlo , ylo = xywhere (x , y , xlolims & everymask )
3437
- caplines .append (mlines .Line2D (xlo , ylo , marker = '|' ,
3438
- ** eb_cap_style ))
3439
-
3441
+ caplines .append (mlines .Line2D (
3442
+ xo , yo , marker = '|' , ** eb_cap_style ))
3440
3443
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 )
3444
+ xo , yo , lo , ro = apply_mask ([x , y , left , right ],
3445
+ xuplims & everymask )
3445
3446
if self .xaxis_inverted ():
3446
3447
marker = mlines .CARETRIGHTBASE
3447
3448
else :
3448
3449
marker = mlines .CARETLEFTBASE
3449
- caplines .append (
3450
- mlines .Line2D (leftlo , ylo , ls = 'None' , marker = marker ,
3451
- ** eb_cap_style ))
3450
+ caplines .append (mlines .Line2D (
3451
+ lo , yo , ls = 'None' , marker = marker , ** eb_cap_style ))
3452
3452
if capsize > 0 :
3453
- xup , yup = xywhere (x , y , xuplims & everymask )
3454
- caplines .append (mlines .Line2D (xup , yup , marker = '|' ,
3455
- ** eb_cap_style ))
3453
+ caplines .append (mlines .Line2D (
3454
+ xo , yo , marker = '|' , ** eb_cap_style ))
3456
3455
3457
3456
if yerr is not None :
3458
- lower , upper = extract_err ('y' , yerr , y )
3457
+ lower , upper = extract_err ('y' , yerr , y , lolims , uplims )
3458
+ barcols .append (self .vlines (
3459
+ * apply_mask ([x , lower , upper ], everymask ), ** eb_lines_style ))
3459
3460
# select points without upper/lower limits in y and
3460
3461
# draw normal errorbars for these points
3461
3462
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
-
3463
+ if noylims .any () and capsize > 0 :
3464
+ xo , lo , uo = apply_mask ([x , lower , upper ], noylims & everymask )
3465
+ caplines .extend ([
3466
+ mlines .Line2D (xo , lo , marker = '_' , ** eb_cap_style ),
3467
+ mlines .Line2D (xo , uo , marker = '_' , ** eb_cap_style )])
3472
3468
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 )
3469
+ xo , yo , lo , uo = apply_mask ([x , y , lower , upper ],
3470
+ lolims & everymask )
3477
3471
if self .yaxis_inverted ():
3478
3472
marker = mlines .CARETDOWNBASE
3479
3473
else :
3480
3474
marker = mlines .CARETUPBASE
3481
- caplines .append (
3482
- mlines .Line2D (xup , upperup , ls = 'None' , marker = marker ,
3483
- ** eb_cap_style ))
3475
+ caplines .append (mlines .Line2D (
3476
+ xo , uo , ls = 'None' , marker = marker , ** eb_cap_style ))
3484
3477
if capsize > 0 :
3485
- xlo , ylo = xywhere (x , y , lolims & everymask )
3486
- caplines .append (mlines .Line2D (xlo , ylo , marker = '_' ,
3487
- ** eb_cap_style ))
3488
-
3478
+ caplines .append (mlines .Line2D (
3479
+ xo , yo , marker = '_' , ** eb_cap_style ))
3489
3480
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 )
3481
+ xo , yo , lo , uo = apply_mask ([x , y , lower , upper ],
3482
+ uplims & everymask )
3494
3483
if self .yaxis_inverted ():
3495
3484
marker = mlines .CARETUPBASE
3496
3485
else :
3497
3486
marker = mlines .CARETDOWNBASE
3498
- caplines .append (
3499
- mlines .Line2D (xlo , lowerlo , ls = 'None' , marker = marker ,
3500
- ** eb_cap_style ))
3487
+ caplines .append (mlines .Line2D (
3488
+ xo , lo , ls = 'None' , marker = marker , ** eb_cap_style ))
3501
3489
if capsize > 0 :
3502
- xup , yup = xywhere ( x , y , uplims & everymask )
3503
- caplines . append ( mlines . Line2D ( xup , yup , marker = '_' ,
3504
- ** eb_cap_style ))
3490
+ caplines . append ( mlines . Line2D (
3491
+ xo , yo , marker = '_' , ** eb_cap_style ))
3492
+
3505
3493
for l in caplines :
3506
3494
self .add_line (l )
3507
3495
3508
3496
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 )
3497
+ errorbar_container = ErrorbarContainer (
3498
+ (data_line , tuple (caplines ), tuple (barcols )),
3499
+ has_xerr = (xerr is not None ), has_yerr = (yerr is not None ),
3500
+ label = label )
3514
3501
self .containers .append (errorbar_container )
3515
3502
3516
3503
return errorbar_container # (l0, caplines, barcols)
0 commit comments