A very-good looking horizontal barplot with several customizations to explore the number of infections caught in laboratories made with `Python` and `Matplotlib`. This blogpost guides you through a step-by-step construction of a horizontal barplot that includes a variety of custom color fonts, labels, and annotations.

Thanks to them for all the inspiring and insightful visualizations! Thanks also to Tomás Capretto who replicated the chart in Python! 🙏🙏

As a teaser, here is the plot we’re gonna try building:

At first sight, one may be tempted to think that today's chart looks rather simple. However, it actually contains several subtle customizations that when added all together make the final result look beautiful. This is also going to be a great opportunity to try an interesting variety of tools from Matplotlib.

``````import numpy as np
import matplotlib.pyplot as plt

from matplotlib import lines
from matplotlib import patches
from matplotlib.patheffects import withStroke``````

## Create data

Let's get started by creating the objects that are going to hold the data for us. Note these values are inferred from the original plot and not something we computed from the original data source.

``````counts = [6, 7, 7, 9, 11, 15, 17, 18, 54]
names = [
"Hantavirus", "Tularemia", "Dengue", "Ebola", "E. coli",
"Tuberculosis", "Salmonella", "Vaccinia", "Brucella"
]

# The positions for the bars
# This allows us to determine exactly where each bar is located
y = [i * 0.9 for i in range(len(names))]``````

And let's also define the colors that are going to be used today.

``````# The colors
BLUE = "#076fa2"
RED = "#E3120B"
BLACK = "#202020"
GREY = "#a2a2a2"``````

## Basic barchart

Creating a horizontal basic barchart in Matplotlib is quite simple. You just pass the locations of the bars and their heights to the `.barh()` method.

In this case, we also set the `height` and the `align` arguments. The first one determines the height of the bars, and the second one means the locations in `y` are used for the edges of the bar.

``````fig, ax = plt.subplots(figsize=(12, 7))

ax.barh(y, counts, height=0.55, align="edge", color=BLUE);``````

You can notice that the bottom edge of the first bar is located at 0, the first value in the list `y`. You can also see the locations aren't given by round numbers. This is to have more control on the height of the bars and their separation.

## Customize layout

The next step is to customize the layout. Here we customize the tickmarks, the spines, the grid lines, and more.

``````ax.xaxis.set_ticks([i * 5 for i in range(0, 12)])
ax.xaxis.set_ticklabels([i * 5 for i in range(0, 12)], size=16, fontfamily="Econ Sans Cnd", fontweight=100)
ax.xaxis.set_tick_params(labelbottom=False, labeltop=True, length=0)

ax.set_xlim((0, 55.5))
ax.set_ylim((0, len(names) * 0.9 - 0.2))

# Set whether axis ticks and gridlines are above or below most artists.
ax.set_axisbelow(True)
ax.grid(axis = "x", color="#A8BAC4", lw=1.2)
ax.spines["right"].set_visible(False)
ax.spines["top"].set_visible(False)
ax.spines["bottom"].set_visible(False)
ax.spines["left"].set_lw(1.5)
# This capstyle determines the lines don't go beyond the limit we specified
# see: https://matplotlib.org/stable/api/_enums_api.html?highlight=capstyle#matplotlib._enums.CapStyle
ax.spines["left"].set_capstyle("butt")

# Hide y labels
ax.yaxis.set_visible(False)

fig
``````

Although there's still work to be done, this is definitely an improvement!

Now it's time to add labels. These represent the name of the infection. Notice that we're going to be used two different colors, depending on whether the name fits in the bar or not. Also, notice the path effect that is added to the labels in blue. Without this effect we would see the grid line at 10 passing behind the text.

``````PAD = 0.3
for name, count, y_pos in zip(names, counts, y):
x = 0
color = "white"
path_effects = None
if count < 8:
x = count
color = BLUE
path_effects=[withStroke(linewidth=6, foreground="white")]

ax.text(
x + PAD, y_pos + 0.5 / 2, name,
color=color, fontfamily="Econ Sans Cnd", fontsize=18, va="center",
path_effects=path_effects
)
fig
``````

## Add annotations and final tweaks

The last step is to add a title, a subtitle, a caption, and other subtleties that really make the difference.

``````# Make room on top and bottom
# Note there's no room on the left and right sides

fig.text(
0, 0.925, "Escape artists",
fontsize=22, fontweight="bold", fontfamily="Econ Sans Cnd"
)
fig.text(
0, 0.875, "Number of laboratory-acquired infections, 1970-2021",
fontsize=20, fontfamily="Econ Sans Cnd"
)

source = "Sources: Laboratory-Acquired Infection Database; American Biological Safety Association"
fig.text(
0, 0.06, source, color=GREY,
fontsize=14, fontfamily="Econ Sans Cnd"
)

fig.text(
0, 0.005, "The Economist", color=GREY,
fontsize=16, fontfamily="Milo TE W01"
)

# Add line and rectangle on top.
fig.add_artist(lines.Line2D([0, 1], [1, 1], lw=3, color=RED, solid_capstyle="butt"))

# Set facecolor, useful when saving as .png
fig.set_facecolor("white")
fig

#fig.savefig("plot.png", dpi=300)``````

And there we got it! It's so elegant 🍸️

## Contact & Edit

👋 This document is a work by Yan Holtz. You can contribute on github, send me a feedback on twitter or subscribe to the newsletter to know when new examples are published! 🔥