0

Consider the sample matplotlib gives for grouped bar charts seen here: https://matplotlib.org/stable/gallery/lines_bars_and_markers/barchart.html

With the code:

import matplotlib.pyplot as plt
import numpy as np


labels = ['G1', 'G2', 'G3', 'G4', 'G5']
men_means = [20, 34, 30, 35, 27]
women_means = [25, 32, 34, 20, 25]

x = np.arange(len(labels))  # the label locations
width = 0.35  # the width of the bars

fig, ax = plt.subplots()
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x, labels)
ax.legend()

ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)

fig.tight_layout()

plt.show()

they produce the following graph:

enter image description here

If I wanted to add the x-axis labels "Men" and "Women" in between G1, G2, G3, G4, and G5 and the bars, for each of the 5 measurements, how would I modify this code?

Stef
  • 28,728
  • 2
  • 24
  • 52
  • 1
    Does this answer your question? [Bar Chart with multiple labels](https://stackoverflow.com/questions/43545879/bar-chart-with-multiple-labels) – Stef Aug 15 '22 at 20:07

1 Answers1

0

Based on the link @Stef provided, here's a working solution.

The patches attribute of your ax object contains the bars themselves, which are matplotlib.patches.Rectangle objects. If you loop through them, you can then use the get_x() and get_width() functions to get the x positions and bar widths, respectively. The x positions of each bar are actually the left side, so you need to add half the bar width to get the center.

sublabel_positions = []

# get_x() returns x position of bar's left side
# add 1/2 the bar width via get_width() to get the center
for bar in ax.patches:
    bar_center = (bar.get_x() + bar.get_width() / 2)
    sublabel_positions.append(bar_center)

As for the labels, since you defined rects1 before rects2 (the men values, then the women values), you want the list of labels to be 5 Men strings, followed by 5 Women strings:

sublabels = ['Men', 'Men', 'Men', 'Men', 'Men', 'Women', 'Women', 'Women', 'Women', 'Women']

# or you could do:
# sublabels = ['Men'] * 5 + ['Women'] * 5

Then you could add the extra x-ticks and labels with a single line shown below. The major x-ticks are your G1 thru G5, so you want to set minor=True here.

ax.set_xticks(sublabel_positions, sublabels, minor=True)

To make it look better, I ended up specifying a larger figure size, padding the major x-tick labels so they don't overlap, and turning off the major x-ticks, and I get this.

x = np.arange(len(labels))  # the label locations
width = 0.35  # the width of the bars

fig, ax = plt.subplots(figsize=(8, 6))
rects1 = ax.bar(x - width/2, men_means, width, label='Men')
rects2 = ax.bar(x + width/2, women_means, width, label='Women')

# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Scores')
ax.set_title('Scores by group and gender')
ax.set_xticks(x, labels)
ax.legend()

ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)

ax.set_xticks(sublabel_positions, sublabels, minor=True)
ax.tick_params(axis='x', which='major', pad=15)
ax.tick_params(axis='x', which='major', bottom=False)

fig.tight_layout()

plt.show()

(I'm sorry I don't have enough reputation to paste images yet)

Finished barplot with 2 series and stacked major/minor x-tick labels

As an aside, if you run ax.get_xticklabels(minor=True), you'll see what I mean about the order of bars being plotted—men first, then women:

>>> ax.get_xticklabels(minor=True)

[Text(-0.175, 0, 'Men'),
 Text(0.825, 0, 'Men'),
 Text(1.825, 0, 'Men'),
 Text(2.825, 0, 'Men'),
 Text(3.825, 0, 'Men'),
 Text(0.175, 0, 'Women'),
 Text(1.175, 0, 'Women'),
 Text(2.175, 0, 'Women'),
 Text(3.175, 0, 'Women'),
 Text(4.175, 0, 'Women')]
dat-t-le
  • 61
  • 4
  • P.S. To be honest, minor x-ticks and labels are unnecessary in this example. The color of the bars and the legend are what help differentiate what's going on. If you wanted to make this example super obvious, plot the men values in blue and the women values in pink (maybe that's an outdated color scheme these days, but it would be effective). But I digress... – dat-t-le Aug 15 '22 at 23:12