header

Table Of Contents

Previous topic

SciPy

Next topic

Pandas

Matplotlib

Overview

We’ve already generated quite a few figures in these lectures using Matplotlib

Matplotlib is interesting in that it provides two fairly different interfaces

The first is designed to mimic MATLAB graphics functionality, and is aimed mainly at beginners

The second is object oriented, more Pythonic and more powerful, but requires some more effort to learn

In this lecture we’ll cover both, with a focus on the second method

The MATLAB-style API

Matplotlib is very easy to get started with, thanks to its simple MATLAB-style API (Application Progamming Interface)

Here’s an example

from pylab import *
x = linspace(0, 10, 200)
y = sin(x)
plot(x, y, 'b-', linewidth=2)
show()

The figure it generates looks as follows

(Source code, png, hires.png, pdf)

_images/pylab_eg.png

If you’ve run these commands inside the IPython notebook with the --pylab inline flag

  • the figure will appear embedded in your browser
  • the from pylab import * line is unnecessary

If you’ve run these commands in IPython without the --pylab inline flag, it will appear as a separate window, like so

_images/sine_fig_screenshot.png

The buttons at the bottom of the window allow you to manipulate the figure and then save it if you wish

Note that the pylab module combines core parts of matplotlib, numpy and scipy

Hence from pylab import * pulls NumPy functions like linspace and sin into the global namespace

Most people start working with Matplotlib using this MATLAB style

The Object-Oriented Approach

The MATLAB style API is simple and convenient, but it’s also a bit limited and somewhat un-Pythonic

For example, in the code above, we use from pylab import *

Here we are pulling lots and lots of names into the global namespace, which is not really a good idea

In particular, with import * we’re pulling in a lot of names and we don’t even know what we’re pulling in

To a trained Python programmer this is a horrible idea

It’s better to be explicit rather than implicit (type import this in the IPython (or Python) shell and look at the second line)

This leads us to the alternative, object oriented API

Here’s the code corresponding to the preceding figure using this second approach

import matplotlib.pyplot as plt
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0, 10, 200)
y = np.sin(x)
ax.plot(x, y, 'b-', linewidth=2)
plt.show()

While there’s a bit more typing, the more explicit declarations will give us far more fine-grained control

This will become more clear as we go along

Incidentally, regarding the above lines of code,

  • the form of the import statement import matplotlib.pyplot as plt is standard

  • Here the call fig, ax = plt.subplots() returns a pair, where

    • fig is a Figure instance—like a blank canvas
    • ax is an AxesSubplot instance—think of a frame for plotting in
  • The plot() function is actually a method of ax

Customization

Here we’ve changed the line to red and added a legend

import matplotlib.pyplot as plt 
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0, 10, 200)
y = np.sin(x)
ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6)
ax.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/sine2.png

We’ve also used alpha to make the line slightly transparent—which makes it look smoother

Unfortunately the legend is obscuring the line

This can be fixed by replacing ax.legend() with ax.legend(loc='upper center')

(Source code, png, hires.png, pdf)

_images/sine3.png

If everthing is properly configured, then adding LaTeX is trivial

import matplotlib.pyplot as plt 
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0, 10, 200)
y = np.sin(x)
ax.plot(x, y, 'r-', linewidth=2, label=r'$y=\sin(x)$', alpha=0.6)
ax.legend(loc='upper center')
plt.show()

The r in front of the label string tells Python that this is a raw string

The figure now looks as follows

(Source code, png, hires.png, pdf)

_images/sine4.png

Controlling the ticks, adding titles and so on is also straightforward

import matplotlib.pyplot as plt 
import numpy as np
fig, ax = plt.subplots()
x = np.linspace(0, 10, 200)
y = np.sin(x)
ax.plot(x, y, 'r-', linewidth=2, label=r'$y=\sin(x)$', alpha=0.6)
ax.legend(loc='upper center')
ax.set_yticks([-1, 0, 1]) 
ax.set_title('Test plot') 
plt.show()

Here’s the figure

(Source code, png, hires.png, pdf)

_images/sine5.png

It’s straightforward to generate mutiple plots on the same axes

Here’s an example that randomly generates three normal densities and adds a label with their mean

import matplotlib.pyplot as plt 
import numpy as np
from scipy.stats import norm
from random import uniform

fig, ax = plt.subplots()
x = np.linspace(-4, 4, 150)
for i in range(3):
    m, s = uniform(-1, 1), uniform(1, 2)
    y = norm.pdf(x, loc=m, scale=s)
    current_label = r'$\mu = {0:.2f}$'.format(m)
    ax.plot(x, y, linewidth=2, alpha=0.6, label=current_label)
ax.legend()
plt.show()

(Source code, png, hires.png, pdf)

_images/nds.png

At other times we want multiple subplots in one figure

Here’s an example that generates 6 histograms

import matplotlib.pyplot as plt 
import numpy as np
from scipy.stats import norm
from random import uniform
num_rows, num_cols = 3, 2
fig, axes = plt.subplots(num_rows, num_cols, figsize=(8, 12))
for i in range(num_rows):
    for j in range(num_cols):
        m, s = uniform(-1, 1), uniform(1, 2)
        x = norm.rvs(loc=m, scale=s, size=100)
        axes[i, j].hist(x, alpha=0.6, bins=20)
        t = r'$\mu = {0:.1f}, \quad \sigma = {1:.1f}$'.format(m, s)
        axes[i, j].set_title(t)
        axes[i, j].set_xticks([-4, 0, 4]) 
        axes[i, j].set_yticks([])
plt.show()

The output looks as follows

(Source code, png, hires.png, pdf)

_images/six_hists.png

In fact the preceding figure was generated by the code above preceded by the following three lines

from matplotlib import rc
rc('font',**{'family':'serif','serif':['Palatino']})
rc('text', usetex=True)

Depending on your LaTeX installation, this may or may not work for you — try experimenting and see how you go

A Customizing Function

Perhaps you will find a set of customizations that you regularly use

Suppose we usually prefer our axes to go through the origin, and to have a grid

Here’s a nice example from this blog of how the object-oriented API can be used to build a custom subplots function that implements these changes

Read carefully through the code and see if you can follow what’s going on

import matplotlib.pyplot as plt
import numpy as np

def subplots():
    "Custom subplots with axes throught the origin"
    fig, ax = plt.subplots()

    # Set the axes through the origin
    for spine in ['left', 'bottom']:
        ax.spines[spine].set_position('zero')
    for spine in ['right', 'top']:
        ax.spines[spine].set_color('none')
    
    ax.grid()
    return fig, ax


fig, ax = subplots()  # Call the local version, not plt.subplots()
x = np.linspace(-2, 10, 200)
y = np.sin(x)
ax.plot(x, y, 'r-', linewidth=2, label='sine function', alpha=0.6)
ax.legend(loc='lower right')
plt.show()

Here’s the figure it produces (note axes through the origin and the grid)

(Source code, png, hires.png, pdf)

_images/subplots.png

The custom subplots function

  1. calls the standard plt.subplots function internally to generate the fig, ax pair,
  2. makes the desired customizations to ax, and
  3. passes the fig, ax pair back to the calling code

Further Reading