Nested objects in matplotplib: Figure, Axes, and Line2D

A little bit of big picture goes a long way

If you remember just these three things, your plotting life will be much easier:

  1. Figures hold Axes
  2. Axes hold almost everything else
  3. Backends do the heavy lifting
  4. 1. Figures hold Axes objects

    In Matplotlib everything is made of objects. There is an object called Figure, and one called Axes. There are objects called Line2D, Patch, Text, Artist, and many others. Some objects are children of others, and members of yet others. It can get hard to keep track of which objects do what.

    The most useful relationship to understand is that Figure objects contain Axes objects. A Figure is just what it sounds like, a rectangular blank canvas on which a plot is constructed. Axes objects are where all the plotting action happens. You can have more than one Axes in a Figure, and you can place them wherever you want.

    fig = pyplot.figure()
    ax = fig.gca()

    In the first line, we use the pyplot interface to initialize a new figure. Then we get the current Axes object with gca(). In this case, this is the Axes object that is created by default when we make our figure.

    2. Axes hold almost everything else

    Aside from providing locations for Axes objects and generating finished plots, a Figure doesn't do much. Most of the action takes place in an Axes object.

    To make this explicit in our code, we will make sure to work on Axes objects directly rather than using the pyplot interface. This helps keep things clear when we have more than one Axes object in a Figure.

    For example, when plotting we’ll use

    ax.plot(x, y)

    Rather than the pyplot.plot(x, y) because the latter obscures the relationship between Axes and plotting.

    Heads up

    The name "Axes" is a little misleading. An Axes object contains both an x-axis and a y-axis (which are objects in their own right), as well as lines, patches, text annotations, and the region in which these are displayed. Throughout these examples, I'll try to be consistent about referring to an Axes object as Axes and an x- and y-axis pair as axes, lowercase.

    3. Backends take care of the complicated parts

    Depending on whether you want to create a file, plot to the screen, or in a web browser, depending on what operating system and graphics drivers you are running, there are lots of of things Matplotlib has to consider when creating your plot. This last mile in making the plot concrete is handled by part of the Matplotlib code called a backend, and as you might imagine, there are lots of them.

    Occasionally not everything plays together perfectly and something won't plot or render correctly on some combination of backend your particular setup. In order to avoid these frustrations, I've found an approach that seems pretty robust. Instead of displaying plots to the screen, I save them as .png files. Then you can look at them using whatever tool you want.

    There is a high-performance backend called AGG (based on the Anti-Grain Geometry Engine) that works on every system and platform I've tried it on. It makes beautiful .png files. This approach is great even if you're working on a headless server and would like to generate visualizations or reports to look at elsewhere.

    import matplotlib
    matplotlib.use("agg")

    Invoking these two lines at the very beginning of a python script sets up the AGG backend. Due to a quirk of the process, if you try to do this after importing pyplot, it will fail to update the backend.

    After setting the backend to "agg", you can proceed as usual to import pyplot and numpy and anything else you might need.

    import matplotlib.pyplot as plt
    import numpy as np

    When it comes time to display a plot, you just need to save it to a file.

    fig.savefig("my_new_plot.png")

    Passing in the path and name of the new image file to savefig() gets the job done.

    Favor clarity over convenience

    To keep things as clean as possible in our code examples, we will usually work directly with the Figure and Axes objects, rather than using the pyplot simplifications that gloss over them. It turns out that if we want to do much customization, this makes our job a lot easier.

    Matplotlib has several ways to do most things, and while this can provide convenience, it can also create a lot of confusion. We will stick to one way of doing things, usually the one that most clearly reflects the underlying structure, rather than the one that results in the shortest code. This also gives us fewer things to remember.

    Method in the madness

    The complexity in Matplotlib is there for good reason. Making attractive interactive images on every type of screen in every operating system is hard. Enabling customization for all our various aesthetics and formatting requirements is also hard. On top of that, making a simplified interface to this complexity that doesn’t scare us all away, but still preserves the most useful functionality is harder still.

    With this in mind, it’s no mean feat that Matplotlib contributors have achieved. Started by John D. Hunter, with the lead developer role passing to Michael Droettboom, then to Thomas Caswell, working with a small army of developers, Matplotlib's contributors have built, maintain, and expand this tool. And every single one of them is a volunteer.

    If you're curious, I recommend this article by Hunter and Droettboom . It gives more of Matplotlib's origin story, as well as more of the details on how it is architected and why.


    With these three ground rules and code patterns in place, we are ready to lay down some straighforward examples. Come take a look.