The basis for all of these topics is the object-oriented interface.

# Object-oriented versus MATLAB styles

We have seen a lot of examples, and in all of them we used the *matplotlib.pyplot* module to create and manipulate the plots, but this is not the only way to make use of the Matplotlib plotting power.

There are three ways to use Matplotlib:

*pyplot*: The module used so far in this article*pylab*: A module to merge Matplotlib and NumPy together in an environment closer to MATLAB- Object-oriented way: The Pythonic way to interface with Matplotlib

Let’s first elaborate a bit about the *pyplot* module: *pyplot* provides a MATLAB-style, procedural, state-machine interface to the underlying object-oriented library in Matplotlib.

A **state machine** is a system with a global status, where each operation performed on the system changes its status.

*matplotlib.pyplot* is stateful because the underlying engine keeps track of the current figure and plotting area information, and plotting functions change that information. To make it clearer, we did not use any object references during our plotting we just issued a pyplot command, and the changes appeared in the figure.

At a higher level, *matplotlib.pyplot* is a collection of commands and functions that make Matplotlib behave like MATLAB (for plotting).

This is really useful when doing interactive sessions, because we can issue a command and see the result immediately, but it has several drawbacks when we need something more such as low-level customization or application embedding.

If we remember, Matplotlib started as an alternative to MATLAB, where we have at hand both numerical and plotting functions. A similar interface exists for Matplotlib, and its name is *pylab*.

*pylab* (do you see the similarity in the names?) is a companion module, installed next to *matplotlib* that merges *matplotlib.pyplot* (for plotting) and *numpy* (for mathematical functions) modules in a single namespace to provide an environment as near to MATLAB as possible, so that the transition would be easy.

We and the authors of Matplotlib discourage the use of *pylab*, other than for proof-of-concept snippets. While being rather simple to use, it teaches developers the wrong way to use Matplotlib.

The third way to use Matplotlib is through the **object-oriented** interface (**OO**, from now on). This is the most powerful way to write Matplotlib code because it allows for complete control of the result however it is also the most complex. This is the Pythonic way to use Matplotlib, and it’s highly encouraged when programming with Matplotlib rather than working interactively. We will use it a lot from now on as it’s needed to go down deep into Matplotlib.

Please allow us to highlight again the preferred style that the author of this article, and the authors of Matplotlib want to enforce: a bit of *pyplot* will be used, in particular for convenience functions, and the remaining plotting code is either done with the OO style or with *pyplot*, with *numpy* explicitly imported and used for numerical functions.

In this preferred style, the initial imports are:

import matplotlib.pyplot as plt

import numpy as np

In this way, we know exactly which module the function we use comes from (due to the module prefix), and it’s exactly what we’ve always done in the code so far.

Now, let’s present the same piece of code expressed in the three possible forms which we just described.

First, we present it in the style, *pyplot* only:

In [1]: import matplotlib.pyplot as plt

In [2]: import numpy as np

In [3]: x = np.arange(0, 10, 0.1)

In [4]: y = np.random.randn(len(x))

In [5]: plt.plot(x, y)

Out[5]: [<matplotlib.lines.Line2D object at 0x1fad810>]In [6]: plt.title('random numbers')

In [7]: plt.show()

The preceding code snippet results in:

Now, let’s see how we can do the same thing using the *pylab* interface:

$ ipython -pylab

...

In [1]: x = arange(0, 10, 0.1)

In [2]: y = randn(len(x))

In [3]: plot(x, y)

Out[3]: [<matplotlib.lines.Line2D object at 0x4284dd0>]

In [4]: title('random numbers')

In [5]: show()

Note that:

ipython -pylab

is not the same as running ipython and then:

from pylab import *

This is because *ipython’s-pylab* switch, in addition to importing everything from pylab, also enables a specific ipython threading mode so that both the interactive interpreter and the plot window can be active at the same time.

Finally, lets make the same chart by using OO style, but with some *pyplot* convenience functions:

In [1]: import matplotlib.pyplot as plt

In [2]: import numpy as np

In [3]: x = np.arange(0, 10, 0.1)

In [4]: y = np.random.randn(len(x))

In [5]: fig = plt.figure()

In [6]: ax = fig.add_subplot(111)

In [7]: l, = plt.plot(x, y)

In [8]: t = ax.set_title('random numbers')

In [9]: plt.show()

The *pylab* code is the simplest, and ,*pyplot* is in the middle, while the OO is the most complex or verbose.

As the Python Zen teaches us, “Explicit is better than implicit” and “Simple is better than complex” and those statements are particularly true for this example: for simple interactive sessions, *pylab* or ,*pyplot* are the perfect choice because they hide a lot of complexity, but if we need something more advanced, then the OO API makes clearer where things are coming from, and what’s going on. This expressiveness will be appreciated when we will embed Matplotlib inside GUI applications.

From now on, we will start presenting our code using the OO interface mixed with some *pyplot* functions.

## A brief introduction to Matplotlib objects

Before we can go on in a productive way, we need to briefly introduce which Matplotlib objects compose a figure.

Let’s see from the higher levels to the lower ones how objects are nested:

Object |
Description |

FigureCanvas |
Container class for the Figure instance |

Figure |
Container for one or more Axes instances |

Axes |
The rectangular areas to hold the basic elements, such as lines, text, and so on |

## Our first (simple) example of OO Matplotlib

In the previous pieces of code, we had transformed this:

...

In [5]: plt.plot(x, y)

Out[5]: [<matplotlib.lines.Line2D object at 0x1fad810>]...

into:

...

In [7]: l, = plt.plot(x, y)

...

The new code uses an explicit reference, allowing a lot more customizations. As we can see in the first piece of code, the plot() function returns a list of *Line2D* instances, one for each line (in this case, there is only one), so in the second code, *l* is a reference to the line object, so every operation allowed on *Line2D* can be done using *l*.

For example, we can set the line color with:

l.set_color('red')

Instead of using the keyword argument to *plot()*, so the line information can be changed after the *plot()* call.

# Subplots

In the previous section, we have seen a couple of important functions without introducing them. Let’s have a look at them now:

*fig = plt.figure()*: This function returns a*Figure*, where we can add one or more*Axes*instances.*ax = fig.add_subplot(111)*: This function returns an*Axes*instance, where we can plot (as done so far), and this is also the reason why we call the variable referring to that instance ax (from*Axes*). This is a common way to add an*Axes to a*Figure, but*add_subplot()*does a bit more: it adds a**subplot**. So far we have only seen a*Figure*with one*Axes*instance, so only one area where we can draw, but Matplotlib allows more than one.

*add_subplot()* takes three parameters:

fig.add_subplot(numrows, numcols, fignum)

where:

*numrows*represents the number of rows of subplots to prepare*numcols*represents the number of columns of subplots to prepare*fignum*varies from*1*to*numrows*numcols*and specifies the current subplot (the one used now)

Basically, we describe a matrix of *numrows*numcols* subplots that we want into the *Figure*; please note that *fignum* is *1* at the upper-left corner of the *Figure* and it’s equal to *numrows*numcols* at the bottom-right corner. The following table should provide a visual explanation of this:

numrows=2, numcols=2, fignum=1 |
numrows=2, numcols=2, fignum=2 |

numrows=2, numcols=2, fignum=3 |
numrows=2, numcols=2, fignum=4 |