1.5. matplotlib - Plotting in Python#

Matplotlib has advantages:

  • Easy to get started (MATLAB-like interface)

  • Support for LaTeX formatted labels and texts

  • Output in many formats, including PNG, PDF, SVG, EPS, and PGF.

  • Extensive gallery of examples with source code (https://matplotlib.org/gallery.html)

  • Programmatic control over all aspects of figures

Programmatic control is a blessing and a curse…

Other plotting tools are available (Plotly, Bokeh, D3, …) but matplotlib is the workhorse.

Matplotlib can be used in two ways:

  • pylab modules (works like MATLAB)

  • object-oreinted interface (harder but more powerful)

%matplotlib inline

1.5.1. MATLAB-like API#

The easiest way to get started with plotting using matplotlib is often to use the MATLAB-like API provided by matplotlib.

It is designed to be compatible with MATLAB’s plotting functions, so it is easy to get started with if you are familiar with MATLAB.

To use this API from matplotlib, we need to include the symbols in the pylab module:

from pylab import *

x = np.linspace(0, 5, 10)
y = x ** 2

figure()
plot(x, y, 'r')
xlabel('x')
ylabel('y')
title('title')
show()

Most of the plotting related functions in MATLAB are covered by the pylab module. For example, subplot and color/symbol selection:

subplot(1,2,1)
plot(x, y, 'r--')
subplot(1,2,2)
plot(y, x, 'g*-');

1.5.2. The matplotlib object-oriented interface#

The pylab interface is easy, but limited.

  • Use simple global functions that match with MATLAB

  • Objects are implicitly defined and hidden from users.

The pyplot object-oriented interface is harder to learn, but much more powerful.

  • Use objects instead of global functions.

  • Explicitly define objects - much better for multiple figures.

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
fig = plt.figure()

axes = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # left, bottom, width, height (range 0 to 1)

axes.plot(x, y, 'r')

axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('title');
fig = plt.figure()

axes1 = fig.add_axes([0.1, 0.1, 0.8, 0.8]) # main axes
axes2 = fig.add_axes([0.2, 0.5, 0.4, 0.3]) # inset axes

# main figure
axes1.plot(x, y, 'r')
axes1.set_xlabel('x')
axes1.set_ylabel('y')
axes1.set_title('title')

# insert
axes2.plot(y, x, 'g')
axes2.set_xlabel('y')
axes2.set_ylabel('x')
axes2.set_title('insert title');

This gives us lots of control, but can also be inconvenient. There are tools to make life a little easier, like subplots:

fig, axes = plt.subplots()

axes.plot(x, y, 'r')
axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('title');
fig, axes = plt.subplots(nrows=1, ncols=2)

for ax in axes:
    ax.plot(x, y, 'r')
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_title('title')
    
#fig.tight_layout()

1.5.2.1. Figure size, aspect ratio and DPI#

Matplotlib allows control of:

  • figure size (aspect ration)

  • dpi (resolution)

when the figure is created. This also works with subplots. If you are planning to put the figure into a report/manuscript it is worthwhile to set this!

fig = plt.figure(figsize=(8,4), dpi=100)

#OR 

fig, axes = plt.subplots(figsize=(8,4))

axes.plot(x, y, 'r')
axes.set_xlabel('x')
axes.set_ylabel('y')
axes.set_title('title')  

1.5.2.2. Saving figures#

To save a figure to a file we can use the savefig method in the Figure class. You can output in many formats, but the most common are:

  • PNG (raster)

  • JPG (raster)

  • SVG (vector)

  • PDF (vector)

The SVG and PDF formats are great because they can be edited afterward with vector graphics programs like Inkscape or Adobe Illustrator.

# if saving rasterized images you can set the dpi (300 is standard)
fig.savefig("filename.png", dpi=300)

fig.savefig("filename.pdf")

1.5.2.3. Titles, labels, and legends#

You should always label axes (including units!), and legends are useful if you have many things plotted at once. You can also set the title of each axis. Note that these are controlled by the axes object, not the Figure object.

axes.set_title("title")
axes.set_xlabel("x [units]")
axes.set_ylabel("y [units]")

fig

Legends

Legends for curves in a figure can be added in two ways.

  • legend method of the axis object

  • label argument of plot

axes.legend(["curve1"]);
fig
axes.plot(x, x**2, label="curve1")
axes.plot(x, x**3, label="curve2")
axes.legend()

fig
axes.legend(loc=0) # let matplotlib decide the optimal location
axes.legend(loc=1) # upper right corner
axes.legend(loc=2) # upper left corner
axes.legend(loc=3) # lower left corner
axes.legend(loc=4) # lower right corner
# .. many more options are available
fig

Putting it all together…

fig, ax = plt.subplots()

ax.plot(x, x**2, label="y = x**2")
ax.plot(x, x**3, label="y = x**3")
ax.legend(loc=2); # upper left corner
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_title('title');

1.5.2.4. Setting colors, linewidths, linetypes#

When plotting a line the following keyword arguments control its properties:

  • color (r, g, b, k, y, m, #000000,…)

  • alpha (0 to 1 for transparancy)

  • marker (‘o’,’+’,’^’,…)

  • linestyle or ls (‘-’, ‘:’, ‘–’, …)

  • linewidth or lw (1, 2, 3, 4 …)

fig, ax = plt.subplots()

ax.plot(x, x+1, color="red", alpha=0.5) # half-transparant red
ax.plot(x, x+2, color="#1155dd")        # RGB hex code for a bluish color
ax.plot(x, x+3, color="#15cc55")        # RGB hex code for a greenish color
ax.plot(x, x+4, color='b', marker='^', ls='--') #dashed blue line with triangle markers

There is also a Matlab-like syntax for simple plots:

ax.plot(x, x+5, '-ok')        # black line with circle markers
fig

Lots more examples…

fig, ax = plt.subplots(figsize=(12,6))

ax.plot(x, x+1, color="blue", linewidth=0.25)
ax.plot(x, x+2, color="blue", linewidth=0.50)
ax.plot(x, x+3, color="blue", linewidth=1.00)
ax.plot(x, x+4, color="blue", linewidth=2.00)

# possible linestype options ‘-‘, ‘--’, ‘-.’, ‘:’, ‘steps’
ax.plot(x, x+5, color="red", lw=2, linestyle='-')
ax.plot(x, x+6, color="red", lw=2, ls='-.')
ax.plot(x, x+7, color="red", lw=2, ls=':')

# possible marker symbols: marker = '+', 'o', '*', 's', ',', '.', '1', '2', '3', '4', ...
ax.plot(x, x+ 8, color="green", lw=2, ls='--', marker='+')
ax.plot(x, x+9, color="green", lw=2, ls='--', marker='o')
ax.plot(x, x+10, color="green", lw=2, ls='--', marker='s')
ax.plot(x, x+11, color="green", lw=2, ls='--', marker='1')

# marker size and color
ax.plot(x, x+12, color="purple", lw=1, ls='-', marker='o', markersize=2)
ax.plot(x, x+13, color="purple", lw=1, ls='-', marker='o', markersize=4)
ax.plot(x, x+14, color="purple", lw=1, ls='-', marker='o', markersize=8, markerfacecolor="red")
ax.plot(x, x+15, color="purple", lw=1, ls='-', marker='s', markersize=8, 
        markerfacecolor="yellow", markeredgewidth=2, markeredgecolor="blue");

1.5.2.5. Control over axis appearance#

There are many options for modifying axis appearance. The most basic one is controlling the range of the plot. You can use:

  • set_xlim - control x range

  • set_ylim - control y range

  • axis('tight') - automaticlly “tighten” the axis range.

You can also control tick positions, tick labels, log scale, etc. See the many examples online.

fig, axes = plt.subplots(1, 3, figsize=(12, 4))

axes[0].plot(x, x**2, x, x**3)
axes[0].set_title("default axes ranges")

axes[1].plot(x, x**2, x, x**3)
axes[1].axis('tight')
axes[1].set_title("tight axes")

axes[2].plot(x, x**2, x, x**3)
axes[2].set_ylim([0, 0.4])
axes[2].set_xlim([0.25, 0.6])
axes[2].set_title("custom axes range");

1.5.2.6. Twin axes#

Sometimes it is useful to have dual x or y axes in a figure; for example, when plotting curves with different units together. Matplotlib supports this with the twinx and twiny functions:

fig, ax1 = plt.subplots()

ax1.plot(x, x**2, lw=2, color="blue")
ax1.set_ylabel(r"area $(m^2)$", fontsize=18, color="blue") #note support for LaTeX-style formatting
for label in ax1.get_yticklabels(): #an example of controlling tick label color
    label.set_color("blue")
    
ax2 = ax1.twinx()
ax2.plot(x, x**3, lw=2, color="red")
ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red")
for label in ax2.get_yticklabels():
    label.set_color("red")

1.5.2.7. Text annotation#

Often it is useful to place annotations on a plot. There are two options:

  • text - place text at an absolute position

  • annotate - place text at a position relative to another point

annotate is useful if you want to draw arrows.

fig, ax = plt.subplots()

t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
ax.plot(t, s, lw=2)

ax.text(s='this is a sin curve', x=0, y=1.3)

ax.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
            arrowprops=dict(facecolor='black', shrink=0.05),
            )

#ax.set_ylim(-2,2)

1.5.2.8. Colormap and contour figures#

Colormaps and contour figures are useful for plotting functions of two variables. In most of these functions we will use a colormap to encode one dimension of the data. There are a number of predefined colormaps. It is relatively straightforward to define custom colormaps. For a list of pre-defined colormaps, see: http://www.scipy.org/Cookbook/Matplotlib/Show_colormaps

Plotting 3D data can be done with:

  • pcolor (show directly with x, y)

  • contour (contour lines with x, y)

  • imshow (interpolate and transpose)

See this discussion of choosing color maps. The “perceptually uniform” color maps are preferred since they work with greyscale and are easier for colorblind people.

#cook up some 3D data to plot

alpha = 0.7
phi_ext = 2 * np.pi * 0.5

def flux_qubit_potential(phi_m, phi_p):
    return 2 + alpha - 2 * np.cos(phi_p) * np.cos(phi_m) - alpha * np.cos(phi_ext - 2*phi_p)

phi_m = np.linspace(0, 2*np.pi, 100)
phi_p = np.linspace(0, 2*np.pi, 100)
X,Y = np.meshgrid(phi_p, phi_m)
Z = flux_qubit_potential(X, Y).T
from matplotlib.cm import jet, RdBu, gray, viridis, plasma

fig, ax = plt.subplots()

p = ax.pcolor(X, Y, Z, cmap=viridis)#, vmin=abs(Z).min(), vmax=abs(Z).max())
cb = fig.colorbar(p, ax=ax)
fig, ax = plt.subplots()

cnt = ax.contour(X, Y, Z, cmap=viridis)#, vmin=abs(Z).min(), vmax=abs(Z).max())
cb = fig.colorbar(cnt, ax=ax)
fig, ax = plt.subplots()

im = ax.imshow(Z, cmap=viridis, vmin=abs(Z).min(), vmax=abs(Z).max(), extent=[0, 2, 0, 1])
im.set_interpolation('bilinear')

cb = fig.colorbar(im, ax=ax)

1.5.2.9. Images#

Matplotlib treats images as numpy arrays, which makes them easy to analyze. There is a toolkit designed specifically for images (matplotlib.image)

import matplotlib.image as mpimg

img = mpimg.imread('bug.jpg') #we can read png or jpg
#print(img)
fig,ax = plt.subplots()
ax.imshow(img)

Images are 3D arrays with x,y,RGB

print(img.shape)
ax.imshow(img[:,:,1],cmap=gray)
fig

It is easy to look at histograms:

fig, axes = plt.subplots(1,2)
axes[0].imshow(img)
axes[0].set_title('image')

axes[1].hist(img[:,:,0].ravel(), bins=256)
axes[1].set_title('color histogram')

#How can we look at the greyscale histogram?

#fig.tight_layout()

1.5.3. 3D figures#

Matplotlib can make 3D plots, but its not easy. I recommend searching the gallery for examples if you really think you need a 3D plot.

from mpl_toolkits.mplot3d import axes3d
import matplotlib.pyplot as plt
from matplotlib import cm

fig = plt.figure()
ax = fig.gca(projection='3d')
X, Y, Z = axes3d.get_test_data(0.05)
ax.plot_surface(X, Y, Z, rstride=8, cstride=8, alpha=0.3)
cset = ax.contourf(X, Y, Z, zdir='z', offset=-100, cmap=viridis)
cset = ax.contourf(X, Y, Z, zdir='x', offset=-40, cmap=viridis)
cset = ax.contourf(X, Y, Z, zdir='y', offset=40, cmap=viridis)

ax.set_xlabel('X')
ax.set_xlim(-40, 40)
ax.set_ylabel('Y')
ax.set_ylim(-40, 40)
ax.set_zlabel('Z')
ax.set_zlim(-100, 100)

plt.show()

1.5.4. Further reading#

Lectures 3+4 of Johanssen: jrjohansson/scientific-python-lectures

Scipy:

Matplotlib:

1.5.5. Versions#

%reload_ext version_information
%version_information numpy, scipy, matplotlib