@@ -3013,7 +3013,8 @@ def broken_barh(self, xranges, yrange, **kwargs):
3013
3013
3014
3014
return col
3015
3015
3016
- def grouped_bar (self , x , heights , dataset_labels = None ):
3016
+ def grouped_bar (self , x , heights , * , group_spacing = 1.5 , bar_spacing = 0 ,
3017
+ dataset_labels = None , orientation = "vertical" ):
3017
3018
"""
3018
3019
Parameters
3019
3020
-----------
@@ -3069,9 +3070,20 @@ def grouped_bar(self, x, heights, dataset_labels=None):
3069
3070
3070
3071
An iterable of array-like: The iteration runs over the groups.
3071
3072
Each individual array-like is the list of label values for that group.
3073
+
3074
+ group_spacing : float
3075
+ The space between two bar groups in units of bar width.
3076
+
3077
+ bar_spacing : float
3078
+ The space between bars in units of bar width.
3079
+
3072
3080
dataset_labels : array-like of str, optional
3073
3081
The labels of the datasets.
3082
+
3083
+ orientation : {"vertical", "horizontal"}, default: vertical
3074
3084
"""
3085
+ _api .check_in_list (["vertical" , "horizontal" ], orientation = orientation )
3086
+
3075
3087
if hasattr (heights , 'keys' ):
3076
3088
if dataset_labels is not None :
3077
3089
raise ValueError (
@@ -3087,11 +3099,15 @@ def grouped_bar(self, x, heights, dataset_labels=None):
3087
3099
if isinstance (x [0 ], str ):
3088
3100
tick_labels = x
3089
3101
group_centers = np .arange (num_groups )
3102
+ group_distance = 1
3090
3103
else :
3091
3104
if num_groups > 1 :
3092
3105
d = np .diff (x )
3093
3106
if not np .allclose (d , d .mean ()):
3094
3107
raise ValueError ("'x' must be equidistant" )
3108
+ group_distance = d [0 ]
3109
+ else :
3110
+ group_distance = 1
3095
3111
group_centers = np .asarray (x )
3096
3112
tick_labels = None
3097
3113
@@ -3102,19 +3118,33 @@ def grouped_bar(self, x, heights, dataset_labels=None):
3102
3118
f"has { len (dataset )} groups"
3103
3119
)
3104
3120
3105
- margin = 0.1
3106
- bar_width = (1 - 2 * margin ) / num_datasets
3121
+ bar_width = (group_distance /
3122
+ (num_datasets + (num_datasets - 1 ) * bar_spacing + group_spacing ))
3123
+ bar_spacing_abs = bar_spacing * bar_width
3124
+ margin_abs = 0.5 * group_spacing * bar_width
3107
3125
3108
3126
if dataset_labels is None :
3109
3127
dataset_labels = [None ] * num_datasets
3110
3128
else :
3111
3129
assert len (dataset_labels ) == num_datasets
3112
3130
3131
+ # place the bars, but only use numerical positions, categorical tick labels
3132
+ # are handled separately below
3113
3133
for i , (hs , dataset_label ) in enumerate (zip (heights , dataset_labels )):
3114
- lefts = group_centers - 0.5 + margin + i * bar_width
3115
- self .bar (lefts , hs , width = bar_width , align = "edge" , label = dataset_label )
3134
+ lefts = (group_centers - 0.5 * group_distance + margin_abs
3135
+ + i * (bar_width + bar_spacing_abs ))
3136
+ if orientation == "vertical" :
3137
+ self .bar (lefts , hs , width = bar_width , align = "edge" ,
3138
+ label = dataset_label )
3139
+ else :
3140
+ self .barh (lefts , hs , height = bar_width , align = "edge" ,
3141
+ label = dataset_label )
3116
3142
3117
- self .xaxis .set_ticks (group_centers , labels = tick_labels )
3143
+ if tick_labels is not None :
3144
+ if orientation == "vertical" :
3145
+ self .xaxis .set_ticks (group_centers , labels = tick_labels )
3146
+ else :
3147
+ self .yaxis .set_ticks (group_centers , labels = tick_labels )
3118
3148
3119
3149
# TODO: does not return anything for now
3120
3150
0 commit comments