@@ -3048,7 +3048,8 @@ def broken_barh(self, xranges, yrange, **kwargs):
30483048
30493049 return col
30503050
3051- def grouped_bar (self , x , heights , dataset_labels = None ):
3051+ def grouped_bar (self , x , heights , * , group_spacing = 1.5 , bar_spacing = 0 ,
3052+ dataset_labels = None , orientation = "vertical" ):
30523053 """
30533054 Parameters
30543055 -----------
@@ -3104,9 +3105,20 @@ def grouped_bar(self, x, heights, dataset_labels=None):
31043105
31053106 An iterable of array-like: The iteration runs over the groups.
31063107 Each individual array-like is the list of label values for that group.
3108+
3109+ group_spacing : float
3110+ The space between two bar groups in units of bar width.
3111+
3112+ bar_spacing : float
3113+ The space between bars in units of bar width.
3114+
31073115 dataset_labels : array-like of str, optional
31083116 The labels of the datasets.
3117+
3118+ orientation : {"vertical", "horizontal"}, default: vertical
31093119 """
3120+ _api .check_in_list (["vertical" , "horizontal" ], orientation = orientation )
3121+
31103122 if hasattr (heights , 'keys' ):
31113123 if dataset_labels is not None :
31123124 raise ValueError (
@@ -3122,11 +3134,15 @@ def grouped_bar(self, x, heights, dataset_labels=None):
31223134 if isinstance (x [0 ], str ):
31233135 tick_labels = x
31243136 group_centers = np .arange (num_groups )
3137+ group_distance = 1
31253138 else :
31263139 if num_groups > 1 :
31273140 d = np .diff (x )
31283141 if not np .allclose (d , d .mean ()):
31293142 raise ValueError ("'x' must be equidistant" )
3143+ group_distance = d [0 ]
3144+ else :
3145+ group_distance = 1
31303146 group_centers = np .asarray (x )
31313147 tick_labels = None
31323148
@@ -3137,19 +3153,33 @@ def grouped_bar(self, x, heights, dataset_labels=None):
31373153 f"has { len (dataset )} groups"
31383154 )
31393155
3140- margin = 0.1
3141- bar_width = (1 - 2 * margin ) / num_datasets
3156+ bar_width = (group_distance /
3157+ (num_datasets + (num_datasets - 1 ) * bar_spacing + group_spacing ))
3158+ bar_spacing_abs = bar_spacing * bar_width
3159+ margin_abs = 0.5 * group_spacing * bar_width
31423160
31433161 if dataset_labels is None :
31443162 dataset_labels = [None ] * num_datasets
31453163 else :
31463164 assert len (dataset_labels ) == num_datasets
31473165
3166+ # place the bars, but only use numerical positions, categorical tick labels
3167+ # are handled separately below
31483168 for i , (hs , dataset_label ) in enumerate (zip (heights , dataset_labels )):
3149- lefts = group_centers - 0.5 + margin + i * bar_width
3150- self .bar (lefts , hs , width = bar_width , align = "edge" , label = dataset_label )
3169+ lefts = (group_centers - 0.5 * group_distance + margin_abs
3170+ + i * (bar_width + bar_spacing_abs ))
3171+ if orientation == "vertical" :
3172+ self .bar (lefts , hs , width = bar_width , align = "edge" ,
3173+ label = dataset_label )
3174+ else :
3175+ self .barh (lefts , hs , height = bar_width , align = "edge" ,
3176+ label = dataset_label )
31513177
3152- self .xaxis .set_ticks (group_centers , labels = tick_labels )
3178+ if tick_labels is not None :
3179+ if orientation == "vertical" :
3180+ self .xaxis .set_ticks (group_centers , labels = tick_labels )
3181+ else :
3182+ self .yaxis .set_ticks (group_centers , labels = tick_labels )
31533183
31543184 # TODO: does not return anything for now
31553185
0 commit comments