@@ -3765,15 +3765,16 @@ def apply_mask(arrays, mask):
3765
3765
return errorbar_container # (l0, caplines, barcols)
3766
3766
3767
3767
@_preprocess_data ()
3768
+ @_api .rename_parameter ("3.9" , "labels" , "tick_labels" )
3768
3769
def boxplot (self , x , notch = None , sym = None , vert = None , whis = None ,
3769
3770
positions = None , widths = None , patch_artist = None ,
3770
3771
bootstrap = None , usermedians = None , conf_intervals = None ,
3771
3772
meanline = None , showmeans = None , showcaps = None ,
3772
3773
showbox = None , showfliers = None , boxprops = None ,
3773
- labels = None , flierprops = None , medianprops = None ,
3774
+ tick_labels = None , flierprops = None , medianprops = None ,
3774
3775
meanprops = None , capprops = None , whiskerprops = None ,
3775
3776
manage_ticks = True , autorange = False , zorder = None ,
3776
- capwidths = None ):
3777
+ capwidths = None , label = None ):
3777
3778
"""
3778
3779
Draw a box and whisker plot.
3779
3780
@@ -3884,9 +3885,10 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
3884
3885
If `False` produces boxes with the Line2D artist. Otherwise,
3885
3886
boxes are drawn with Patch artists.
3886
3887
3887
- labels : sequence, optional
3888
- Labels for each dataset (one per dataset). These are used for
3889
- x-tick labels; *not* for legend entries.
3888
+ tick_labels : sequence, optional
3889
+ Labels for each dataset (one per dataset).
3890
+
3891
+ .. versionadded:: 3.9
3890
3892
3891
3893
manage_ticks : bool, default: True
3892
3894
If True, the tick locations and labels will be adjusted to match
@@ -3954,6 +3956,11 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
3954
3956
The style of the median.
3955
3957
meanprops : dict, default: None
3956
3958
The style of the mean.
3959
+ label : str or list of str, optional
3960
+ Legend labels for each boxplot.
3961
+
3962
+ .. versionadded:: 3.9
3963
+
3957
3964
data : indexable object, optional
3958
3965
DATA_PARAMETER_PLACEHOLDER
3959
3966
@@ -3970,7 +3977,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
3970
3977
bootstrap = mpl .rcParams ['boxplot.bootstrap' ]
3971
3978
3972
3979
bxpstats = cbook .boxplot_stats (x , whis = whis , bootstrap = bootstrap ,
3973
- labels = labels , autorange = autorange )
3980
+ tick_labels = tick_labels , autorange = autorange )
3974
3981
if notch is None :
3975
3982
notch = mpl .rcParams ['boxplot.notch' ]
3976
3983
if vert is None :
@@ -4006,6 +4013,9 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
4006
4013
if 'color' in boxprops :
4007
4014
boxprops ['edgecolor' ] = boxprops .pop ('color' )
4008
4015
4016
+ if label :
4017
+ boxprops ['label' ] = label
4018
+
4009
4019
# if non-default sym value, put it into the flier dictionary
4010
4020
# the logic for providing the default symbol ('b+') now lives
4011
4021
# in bxp in the initial value of flierkw
@@ -4074,7 +4084,7 @@ def boxplot(self, x, notch=None, sym=None, vert=None, whis=None,
4074
4084
meanline = meanline , showfliers = showfliers ,
4075
4085
capprops = capprops , whiskerprops = whiskerprops ,
4076
4086
manage_ticks = manage_ticks , zorder = zorder ,
4077
- capwidths = capwidths )
4087
+ capwidths = capwidths , label = label )
4078
4088
return artists
4079
4089
4080
4090
def bxp (self , bxpstats , positions = None , widths = None , vert = True ,
@@ -4083,7 +4093,7 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True,
4083
4093
boxprops = None , whiskerprops = None , flierprops = None ,
4084
4094
medianprops = None , capprops = None , meanprops = None ,
4085
4095
meanline = False , manage_ticks = True , zorder = None ,
4086
- capwidths = None ):
4096
+ capwidths = None , label = None ):
4087
4097
"""
4088
4098
Draw a box and whisker plot from pre-computed statistics.
4089
4099
@@ -4169,6 +4179,9 @@ def bxp(self, bxpstats, positions=None, widths=None, vert=True,
4169
4179
zorder : float, default: ``Line2D.zorder = 2``
4170
4180
The zorder of the resulting boxplot.
4171
4181
4182
+ label : str or list of str, optional
4183
+ Legend labels for each boxplot.
4184
+
4172
4185
Returns
4173
4186
-------
4174
4187
dict
@@ -4330,8 +4343,8 @@ def do_patch(xs, ys, **kwargs):
4330
4343
if showbox :
4331
4344
do_box = do_patch if patch_artist else do_plot
4332
4345
boxes .append (do_box (box_x , box_y , ** box_kw ))
4346
+ whisker_kw .setdefault ('label' , '_nolegend_' )
4333
4347
# draw the whiskers
4334
- whisker_kw .setdefault ('label' , '_nolegend_' )
4335
4348
whiskers .append (do_plot (whis_x , whislo_y , ** whisker_kw ))
4336
4349
whiskers .append (do_plot (whis_x , whishi_y , ** whisker_kw ))
4337
4350
# maybe draw the caps
@@ -4358,6 +4371,15 @@ def do_patch(xs, ys, **kwargs):
4358
4371
flier_y = stats ['fliers' ]
4359
4372
fliers .append (do_plot (flier_x , flier_y , ** flier_kw ))
4360
4373
4374
+ # Set legend labels
4375
+ if label :
4376
+ box_or_whis = boxes if showbox else whiskers
4377
+ for index , element in enumerate (box_or_whis ):
4378
+ try :
4379
+ element .set_label (label [index ])
4380
+ except Exception :
4381
+ IndexError ('list index out of range' )
4382
+
4361
4383
if manage_ticks :
4362
4384
axis_name = "x" if vert else "y"
4363
4385
interval = getattr (self .dataLim , f"interval{ axis_name } " )
@@ -8205,7 +8227,7 @@ def matshow(self, Z, **kwargs):
8205
8227
@_preprocess_data (replace_names = ["dataset" ])
8206
8228
def violinplot (self , dataset , positions = None , vert = True , widths = 0.5 ,
8207
8229
showmeans = False , showextrema = True , showmedians = False ,
8208
- quantiles = None , points = 100 , bw_method = None ):
8230
+ quantiles = None , points = 100 , bw_method = None , side = 'both' ):
8209
8231
"""
8210
8232
Make a violin plot.
8211
8233
@@ -8256,6 +8278,10 @@ def violinplot(self, dataset, positions=None, vert=True, widths=0.5,
8256
8278
callable, it should take a `matplotlib.mlab.GaussianKDE` instance as
8257
8279
its only parameter and return a float.
8258
8280
8281
+ side : {'both', 'low', 'high'}, default: 'both'
8282
+ 'both' plots standard violins. 'low'/'high' only
8283
+ plots the side below/above the positions value.
8284
+
8259
8285
data : indexable object, optional
8260
8286
DATA_PARAMETER_PLACEHOLDER
8261
8287
@@ -8307,10 +8333,10 @@ def _kde_method(X, coords):
8307
8333
quantiles = quantiles )
8308
8334
return self .violin (vpstats , positions = positions , vert = vert ,
8309
8335
widths = widths , showmeans = showmeans ,
8310
- showextrema = showextrema , showmedians = showmedians )
8336
+ showextrema = showextrema , showmedians = showmedians , side = side )
8311
8337
8312
8338
def violin (self , vpstats , positions = None , vert = True , widths = 0.5 ,
8313
- showmeans = False , showextrema = True , showmedians = False ):
8339
+ showmeans = False , showextrema = True , showmedians = False , side = 'both' ):
8314
8340
"""
8315
8341
Draw a violin plot from pre-computed statistics.
8316
8342
@@ -8366,6 +8392,10 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
8366
8392
showmedians : bool, default: False
8367
8393
Whether to show the median with a line.
8368
8394
8395
+ side : {'both', 'low', 'high'}, default: 'both'
8396
+ 'both' plots standard violins. 'low'/'high' only
8397
+ plots the side below/above the positions value.
8398
+
8369
8399
Returns
8370
8400
-------
8371
8401
dict
@@ -8428,8 +8458,13 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
8428
8458
elif len (widths ) != N :
8429
8459
raise ValueError (datashape_message .format ("widths" ))
8430
8460
8461
+ # Validate side
8462
+ _api .check_in_list (["both" , "low" , "high" ], side = side )
8463
+
8431
8464
# Calculate ranges for statistics lines (shape (2, N)).
8432
- line_ends = [[- 0.25 ], [0.25 ]] * np .array (widths ) + positions
8465
+ line_ends = [[- 0.25 if side in ['both' , 'low' ] else 0 ],
8466
+ [0.25 if side in ['both' , 'high' ] else 0 ]] \
8467
+ * np .array (widths ) + positions
8433
8468
8434
8469
# Colors.
8435
8470
if mpl .rcParams ['_internal.classic_mode' ]:
@@ -8441,20 +8476,34 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
8441
8476
# Check whether we are rendering vertically or horizontally
8442
8477
if vert :
8443
8478
fill = self .fill_betweenx
8444
- perp_lines = functools .partial (self .hlines , colors = linecolor )
8445
- par_lines = functools .partial (self .vlines , colors = linecolor )
8479
+ if side in ['low' , 'high' ]:
8480
+ perp_lines = functools .partial (self .hlines , colors = linecolor ,
8481
+ capstyle = 'projecting' )
8482
+ par_lines = functools .partial (self .vlines , colors = linecolor ,
8483
+ capstyle = 'projecting' )
8484
+ else :
8485
+ perp_lines = functools .partial (self .hlines , colors = linecolor )
8486
+ par_lines = functools .partial (self .vlines , colors = linecolor )
8446
8487
else :
8447
8488
fill = self .fill_between
8448
- perp_lines = functools .partial (self .vlines , colors = linecolor )
8449
- par_lines = functools .partial (self .hlines , colors = linecolor )
8489
+ if side in ['low' , 'high' ]:
8490
+ perp_lines = functools .partial (self .vlines , colors = linecolor ,
8491
+ capstyle = 'projecting' )
8492
+ par_lines = functools .partial (self .hlines , colors = linecolor ,
8493
+ capstyle = 'projecting' )
8494
+ else :
8495
+ perp_lines = functools .partial (self .vlines , colors = linecolor )
8496
+ par_lines = functools .partial (self .hlines , colors = linecolor )
8450
8497
8451
8498
# Render violins
8452
8499
bodies = []
8453
8500
for stats , pos , width in zip (vpstats , positions , widths ):
8454
8501
# The 0.5 factor reflects the fact that we plot from v-p to v+p.
8455
8502
vals = np .array (stats ['vals' ])
8456
8503
vals = 0.5 * width * vals / vals .max ()
8457
- bodies += [fill (stats ['coords' ], - vals + pos , vals + pos ,
8504
+ bodies += [fill (stats ['coords' ],
8505
+ - vals + pos if side in ['both' , 'low' ] else pos ,
8506
+ vals + pos if side in ['both' , 'high' ] else pos ,
8458
8507
facecolor = fillcolor , alpha = 0.3 )]
8459
8508
means .append (stats ['mean' ])
8460
8509
mins .append (stats ['min' ])
0 commit comments