part of Course 133 Navigating Matplotlib


Axes with position set manually by size and position
ax = fig.add_axes(
    (left, bottom, width, height))
multiple Axes on a single Figure
ax1 = fig.add_axes(
    (left_1, bottom_1, width_1, height_1))
ax1.plot(x, y)
ax2 = fig.add_axes(
    (left_2, bottom_2, width_2, height_2))
ax2.plot(x, y)
...
Axes sized to fill the Figure
ax1 = fig.add_axes((0, 0, 1, 1))
some of the Axes spines turned off
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)
spines offset from their default positions
ax.spines["top"].set_position(
    ("outward", 15))
ax.spines["bottom"].set_position(
    ("data", 0))
ax.spines["left"].set_position(
    ("axes", .3)) 
Axes spines with several styles
ax.spines["top"].set_color("orange")
ax.spines["left"].set_bounds(-.5, .5)
ax.spines["right"].set_linestyle("--")
ax.spines["bottom"].set_linewidth(6)
ax.spines["bottom"].set_capstyle("round")
a half-size Figure
fig = plt.figure(figsize=(width, height))
a low resolution version of the image
fig.savefig("filename.png", dpi=15)
a plot with a colorful background
ax.set_facecolor("#e1ddbf")
A Figure with a colorful background
fig = plt.figure(facecolor="#e1ddbf")
fig.savefig(
    "filename.png",
    facecolor=fig.get_facecolor())
a Figure with a solid frame
fig = plt.figure(
    linewidth=10, edgecolor="#04253a")
fig.savefig(
    "filename.png",
    edgecolor=fig.get_edgecolor())


Get your layout right

I've spent more time wrestling with frames, layouts, and backgrounds than anything else in Matplotlib. Here are the most straightforward methods I've found.

First, we'll start with this setup for all the examples to follow. For an explanation of it, check here.

import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np

# Create the curve data.
x = np.linspace(-6, 6, 500)
y = np.sinc(x)

fig = plt.figure()
ax = fig.gca()
ax.plot(x, y)


Manually place your Axes

By default, a Figure will create an Axes object with a reasonable size and location, but it doesn't always get it right. If you change your tick label formatting or tick font size for instance, things can start falling off the edge of the Figure. To get around this, you can manually place Axes right where you wnt them.

ax = fig.add_axes((left, bottom, width, height))

This call to add_axes() specifies the x and y positions of the lower left hand corner of the Axes, together with the width and the height of it. It's most important to note that all of these are expressed as fractions of the figure width and height. Because of this, they will almost always fall between 0 and 1.

Axes with position set manually by size and position

Create several Axes

Some stories are best told by putting two or more plots side by side. In Matplotlib you can do this by adding additional Axes to the same Figure as many times as you need to.

ax1 = fig.add_axes((left_1, bottom_1, width_1, height_1))
ax1.plot(x, y)
ax2 = fig.add_axes((left_2, bottom_2, width_2, height_2))
ax2.plot(x, y)
...

Each one is a separate object and can be modified independently using all the methods we've mentioned already. Axes can overlap each other and even fall partly outside the Figure boundaries.

multiple Axes on a single Figure

I have to admit a bias here. There are already some tools in place to help layout multiple Axes objects. There's subplots() and gridspec(). Those get 80% of the way there. Then there is tight_layout() and constrained layout() which try to clean things up and make up yet another 10%. If you need a 90% solution, these are a fine way to go. But if you want things to look a particular way or line up just so, then you'll soon find yourself tweaking these and fiddling with down-in-the-weeds settings. In my experience, I get better results with less work by thinking through the layout I want and specifying each Axes manually. But if you are feeling curious try both approaches for yourself and see what works best for you.



Fill the Figure with one Axes

One trick that's occasionally useful is to fill the entire Figure with the plot area of one Axes. With our trick of manual placement, this is a breeze.

ax1 = fig.add_axes((0, 0, 1, 1))

Using left and bottom values of 0 and width and height of 1 fills the Axes completely, by definition.

Axes sized to fill the Figure

Hide axis spines

Removing unnecessary lines really cleans up a plot. The lines that form the border of the plot area are called axis spines, and sometimes they are expendable.

ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.spines["left"].set_visible(False)

This trick lets us set the properties of each of the four spines independently. Here, we toggle their visibility.

some of the Axes spines turned off

Move the spines around

We can also shift the spines around a bit. The y-axis spines can move right and left and the x-axis spines can move up and down.

ax.spines["top"].set_position(("outward", 15))
ax.spines["bottom"].set_position(("data", 0))
ax.spines["left"].set_position(("axes", .3))
ax.spines["right"].set_position(("outward", -40))

There are three different methods to move them: "data", "axes", and "outward".

"data" lets you specify the position in your data coordinates to place the spine. This is super useful for putting spines along the x = 0 and y = 0 lines.

"axes" lets you name the position as a fraction of the Axes height and width. 0 is the bottom or the left of the plotting area, and 1 is the top or the right. .5 is right in the middle. Less than 0 or more than 1 is outside the plotting area.

"outward" lets you indicate a position as an offset from the boundary of the plotting area, measured in points (there are 72 points in an inch). A positive offset is away from the center of the plotting area, a negative offset is toward it. A positive offset for a left spine would move it to the left, and a positive offset of a right spine would move it to the right, for example.

Note that all of these let you place spines outside the plotting area (or even outside the Figure boundaries).

spines offset from their default positions

Modify the spines

Just like all the other lines in our plot, we can modify their style to suit our whims.

ax.spines["top"].set_color("orange")
ax.spines["left"].set_bounds(-.5, .5)
ax.spines["right"].set_linestyle("--")
ax.spines["bottom"].set_linewidth(6)
ax.spines["bottom"].set_capstyle("round")

Most of the settings should be familiar from the section on Lines, but it's worth calling out set_bounds(). It allows you to dictate the lower and upper bounds of an axis spine. There's no need for it to span the plotting area.

Axes spines with several styles

Change the size of the Figure

You can also change the measurements of the Figure overall. When you create it without choosing a size, Matplotlib chooses a reasonable default size - typically something like 6 1/2 inches wide by 4 1/2 inches high. However, if you know the final image is going to need to be a certain size, say, filling a 12 x 8 inch spot on a poster or a 3 x 2 inch slot in a two-column paper manuscript, if can be helpful to start with that.

fig = plt.figure(figsize=(width, height))

Here, width and height are always in inches. By starting with the final size specification, you can avoid rescaling your final image. This lets you control the aspect ratio and makes the font sizes and line thicknesses meaningful. It also ensures that you can pick an output resolution that will look sharp.

a half-size Figure

Change the resolution of the Figure

Together with size, controlling the output image resolution gives you control over the quality of the final product.

fig.savefig("image_filename.png", dpi=15)

You can specify the dpi, dots per inch, in the call to savefig(). For printing and most screens, 150 is pretty good, 300 is clear, and 600 is spectacular. 1200 or higher can come in handy if you want to be able to do a lot of zooming in, but your image can start to get very big on disk at that resolution.

a low resolution version of the image

Change Axes background color

It's not tough to change up the background color of the plotting area.

ax.set_facecolor("#e1ddbf")

You can use any Matplotlib color specification for this. Here we used a hexidecimal code.

a plot with a colorful background

Change Figure background color

Similarly, you can tweak the Figure background color to be whatever you like.

fig = plt.figure(facecolor="#e1ddbf")
fig.savefig("image_filename.png", facecolor=fig.get_facecolor())

We can set this up when first creating the Figure by including the facecolor argument and choosing a color.

A Figure with a colorful background

One quirk of this is that savefig() tries to be helpful and assumes that you'd like to save with a white background. While this default has doubtless saved truckloads of toner cartridges, it's not always what you want. To get around this, you can manually re-specify that the facecolor of the saved image background should definitely be the facecolor of the original Figure.



Frame the Figure

Rounding out our menu of layout midifications is a frame around the whole Figure.

fig = plt.figure(linewidth=10, edgecolor="#04253a")
fig.savefig("image_filename.png", edgecolor=fig.get_edgecolor())

We can create a border by both choosing a nonzero linewidth and an edgecolor when creating our Figure. When calling savefig() we have to repeat our trick of explicitly setting the edgecolor in the output image.

a Figure with a solid frame

Now you know all my layout tricks. As with all of Matplotlib, there are several ways to do most of these things, but what I included here are for me the most intuitive, most flexible, and require the least memorization.

If this was helpful, there are a handful of other Matplotlib how-to's you can refer to: come take a look at the full set of tutorials.