COMP0233: Research Software Engineering With Python


Plotting with Matplotlib

Plotting data is very common and useful in scientific work. Python does not include any plotting functionality in the language itself, but there are various frameworks available for producing plots and visualisations.

In this section, we will look at Matplotlib, one of those frameworks. As the name indicates, it was conceived to provide an interface similar to the MATLAB programming language, but no knowledge of MATLAB is required!

Importing Matplotlib

We import the pyplot object from Matplotlib, which provides us with an interface for making figures. We usually abbreviate it.

In [1]:
from matplotlib import pyplot as plt

Notebook magics

When we write:

In [2]:
%matplotlib inline

We tell the Jupyter notebook to show figures we generate alongside the code that created it, rather than in a separate window. Lines beginning with a single percent are not python code: they control how the notebook deals with python code.

Lines beginning with two percents are "cell magics", that tell Jupyter notebook how to interpret the particular cell; we've seen %%writefile, for example.

A basic plot

When we write:

In [3]:
from math import sin, cos, pi
my_fig = plt.plot([sin(pi * x / 100.0) for x in range(100)])
No description has been provided for this image

The plot command returns a figure, just like the return value of any function. The notebook then displays this.

To add a title, axis labels etc, we need to get that figure object, and manipulate it. For convenience, matplotlib allows us to do this just by issuing commands to change the "current figure":

In [4]:
plt.plot([sin(pi * x / 100.0) for x in range(100)])
Text(0.5, 1.0, 'Hello')
No description has been provided for this image

But this requires us to keep all our commands together in a single cell, and makes use of a "global" single "current plot", which, while convenient for quick exploratory sketches, is a bit cumbersome. To produce from our notebook proper plots to use in papers, the library defines some types we can use to treat individual figures as variables, and manipulate these.

Figures and Axes

We often want multiple graphs in a single figure (e.g. for figures which display a matrix of graphs of different variables for comparison).

So Matplotlib divides a figure object up into axes: each pair of axes is one 'subplot'. To make a boring figure with just one pair of axes, however, we can just ask for a default new figure, with brand new axes. The relevant function returns a (figure, axis) pair, which we can deal out with parallel assignment (unpacking).

In [5]:
sine_graph, sine_graph_axes = plt.subplots()
No description has been provided for this image

Once we have some axes, we can plot a graph on them:

In [6]:
sine_graph_axes.plot([sin(pi * x / 100.0) for x in range(100)], label='sin(x)')
[<matplotlib.lines.Line2D at 0x7f40c86de880>]

We can add a title to a pair of axes:

In [7]:
sine_graph_axes.set_title("My graph")
Text(0.5, 1.0, 'My graph')
In [8]:
Text(4.444444444444445, 0.5, 'f(x)')
In [9]:
sine_graph_axes.set_xlabel("100 x")
Text(0.5, 4.444444444444445, '100 x')

Now we need to actually display the figure. As always with the notebook, if we make a variable be returned by the last line of a code cell, it gets displayed:

In [10]:
No description has been provided for this image

We can add another curve:

In [11]:
sine_graph_axes.plot([cos(pi * x / 100.0) for x in range(100)], label='cos(x)')
[<matplotlib.lines.Line2D at 0x7f40c83dfd90>]
In [12]:
No description has been provided for this image

A legend will help us distinguish the curves:

In [13]:
<matplotlib.legend.Legend at 0x7f40c87653a0>
In [14]:
No description has been provided for this image

Saving figures

We must be able to save figures to disk, in order to use them in papers. This is really easy:

In [15]:

In order to be able to check that it worked, we need to know how to display an arbitrary image in the notebook.

The programmatic way is like this:

In [16]:
from IPython.display import Image # Get the notebook's own library for manipulating itself.
No description has been provided for this image


We might have wanted the $\sin$ and $\cos$ graphs on separate axes:

In [17]:
double_graph = plt.figure()
<Figure size 640x480 with 0 Axes>
In [18]:
sin_axes = double_graph.add_subplot(2, 1, 1) # 2 rows, 1 column, 1st subplot
In [19]:
cos_axes = double_graph.add_subplot(2, 1, 2) # 2 rows, 1 column, 2nd subplot
In [20]:
No description has been provided for this image
In [21]:
sin_axes.plot([sin(pi * x / 100.0) for x in range(100)])
[<matplotlib.lines.Line2D at 0x7f40c8312160>]
In [22]:
Text(4.444444444444445, 0.5, 'sin(x)')
In [23]:
cos_axes.plot([cos(pi * x / 100.0) for x in range(100)])
[<matplotlib.lines.Line2D at 0x7f40c82db940>]
In [24]:
Text(4.444444444444445, 0.5, 'cos(x)')
In [25]:
cos_axes.set_xlabel("100 x")
Text(0.5, 4.444444444444445, '100 x')
In [26]:
No description has been provided for this image

Versus plots

When we specify a single list to plot, the x-values are just the array index number. We usually want to plot something more meaningful:

In [27]:
double_graph = plt.figure()
sin_axes = double_graph.add_subplot(2, 1, 1)
cos_axes = double_graph.add_subplot(2, 1, 2)
Text(0.5, 0, 'x')
No description has been provided for this image
In [28]:
sin_axes.plot([x / 100.0 for x in range(100)], [sin(pi * x / 100.0) for x in range(100)])
cos_axes.plot([x / 100.0 for x in range(100)], [cos(pi * x / 100.0) for x in range(100)])
[<matplotlib.lines.Line2D at 0x7f40c81d3400>]
In [29]:
No description has been provided for this image

Learning More

There's so much more to learn about matplotlib: pie charts, bar charts, heat maps, 3-d plotting, animated plots, and so on. You can learn all this via the Matplotlib Website. You should try to get comfortable with all this, so please use some time in class, or at home, to work your way through a bunch of the examples.