Nested horizontal bar charts in Matplotlib

Matplotlib is pretty much the primary library for creating charts in Python. This library has been around for a while and there is a good amount of documentation on using it. However, there are some examples that aren’t very common.

One kind of chart I am using for some data visualization work is to embed a smaller set of bars inside a larger set. This is similar to a stacked bar chart kinda but with a couple of differences that provide some other features in terms of being able to represent relationships in the actual data. This type of chart allows you to convey information about an overall metric and then break it down into component metrics, or perhaps show a related set of metrics right on top of it.

And truth be told another motivating factor is that it can buy you some vertical space in a chart.

Since I haven’t found many examples of this I thought I would post one.

A basic horizontal bar chart

import matplotlib.pyplot as plt

title = "A Basic Horizontal Bar Chart"
bar_labels = ["A", "B", "C", "D", "E"]

# some random data...
mock_data = [1764.498, 819.882, 300.161, 452.0789, 305.345]

fig, ax = plt.subplots()
plt.rcdefaults()
y_pos = range(len(bar_labels))

primary_bar_height = 0.8
ax.barh(y_pos,
        mock_data,
        primary_bar_height,
        align='center',
        color='green',
        ecolor='black')

ax.set_yticks(y_pos)
ax.set_yticklabels(bar_labels)

# Order the labels on the 
# chart from top to bottom
ax.invert_yaxis()

ax.set_xlabel('Count of ... stuff')
ax.set_title(title)
plt.tight_layout()
plt.show()

This will produce the following chart.

basic horizontal bar chart in matplotlib
A basic horizontal bar chart in matplotlib.

Embedding bars within bars.

From the basic horizontal bar chart above it isn’t that far a leap to embedding bars. All we are doing is adding the additional bars with a smaller ‘height’ (you can think of it as width if you prefer) but using the same vertical position as the first bar.

ax.barh(y_pos, <-- Don't add an offset here
        nested_data_p2,
        nested_bar_height,   <-- Make the nested bar smaller.
        color="#29a1d5",
        label="Records Query",
        left=nested_data_p1    <-- Offset the starting point in the x axis
 )

Additionally, for each nested bar after the first one we need to adjust the horizontal starting point.

To illustrate this we’ll add two smaller bars inside each of the main bars from the first chart above. The additional code is highlighted in blue.

import matplotlib.pyplot as plt
from pprint import pprint

title = "A Basic Horizontal Bar Chart"
bar_labels = ["A", "B", "C", "D", "E"]

# some random data...
mock_data = [1764.498, 819.882, 300.161, 452.0789, 305.345]

nested_data_p1 = [134.5, 45.0, 75.4, 128.0, 68.3]
nested_data_p2 = [435.0, 345.0, 105.4, 30.0, 55.3]

fig, ax = plt.subplots()
plt.rcdefaults()
y_pos = range(len(bar_labels))

# Set up the primary bars.
primary_bar_height = 0.8
ax.barh(y_pos,
        mock_data,
        primary_bar_height,
        align='center',
        color='green',
        ecolor='black')

# Now add the first set of nested bars.
nested_bar_height = 0.4
ax.barh(y_pos, 
        nested_data_p1,
        nested_bar_height, 
        color="#f6ff33",
        label="Count Query")

# Add the second set. Adjust the 
# starting point on the left
# side by setting left appropriately. 
# In this case we can just
# use the size of the first set of
# nested bars above.
ax.barh(y_pos,
        nested_data_p2,
        nested_bar_height,
        color="#33ffff",
        label="Records Query",
        left=nested_data_p1)

# And finally complete the chart. This part
# is the same as the basic bar chart.
ax.set_yticks(y_pos)
ax.set_yticklabels(bar_labels)

# Order the labels on the chart from top to bottom
ax.invert_yaxis()

ax.set_xlabel('Count of ... stuff')
ax.set_title(title)
plt.tight_layout()
plt.show()

The resulting chart looks like this:

And that’s it.  There are some other variations that can be done as well.  You aren’t obligated to start the inner bar at the left for instance — you can position that more in the middle of the bar if that makes sense for your data.

Finally, this kind of bar chart is similar to a Bullet chart, as described by Stephen Few. With a bit more work the code above could produce a bullet chart too.