Plotting tutorial (Python)

Matplotlib (MPL) is the default choice, with other options including Seaborn for high-level plotting, Plotly for JS plotting framework, Bokeh for interactive plotting.

Installation

Matplotlib is included in the Anaconda distribution by default.

In case it's not available, just run

conda install matplotlib

Reference

Anatomy of a figure (from mpl official website)

Conventional short names for matplotlib and numpy:

import matplotlib.pyplot as plt
import numpy as np
# For inline plotting in jupyter notebooks
%matplotlib inline  
1.7s
Python

Line plots

Line plots are usually for visualization of 2D data.

e.g. time series (y-t), phase plots (x-y)

plt.plot(xs, ys)

See also

# Data #
x = np.linspace(0, 10, num=100)
y1 = np.sin(x)
y2 = np.cos(x)
# Opens a new figure to be plotted
plt.figure()
# plot(x, y, <MATLAB stylestring>)
plt.plot(x, y1, '-')  
plt.plot(x, y2, '--')
0.7s
Python

Add more things to the plot.

# Let's add some more options
# Set figure (whole picture) size to 10 * 10
plt.figure(figsize = (10, 10))
# Add grid
plt.grid() 
# Title
plt.title("Waves")
# Lables for X & Y axes
plt.xlabel("Time")
plt.ylabel("Amplitude")
# 'o-' does not mean orange line rather than circle dots
# '^' means triangle dots
# line labels are also set
plt.plot(x, y1, '^-', label="Line1", color='orange')  
plt.plot(x, y2, 'b--', label="Line2")
# Show the labels
plt.legend(loc='upper left')
0.8s
Python

Line customization

Multiple series

1 column = 1 series of data

# Data #
x = np.linspace(0, 10, 100)
# 4 columns of data = 4 series
# y = sin(x + 0.5k * pi); k = 0, 1, 2, 3
y = np.sin(x[:, np.newaxis] + np.pi * np.arange(0, 2, 0.5))
y.shape
0.0s
Python
plt.figure()
plt.plot(x, y)
0.6s
Python
plt.figure()
lines = plt.plot(x, y[:, 0:2])
# Another way to set labels
plt.legend(lines, ['First', 'Second'], loc='upper right')
0.5s
Python

Tweaking Axis ticks

  • Logarithmic scale

plt.xscale('log')
plt.tick_params(
    axis='x',          # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom=False,      # ticks along the bottom edge are off
    top=False,         # ticks along the top edge are off
    labelbottom=False) # labels along the bottom edge are off

See also: axes()

plt.tick_params(
    axis='x',          # changes apply to the x-axis
    which='both',      # both major and minor ticks are affected
    bottom=False,      # ticks along the bottom edge are off
    top=False,         # ticks along the top edge are off
    labelbottom=False) # labels along the bottom edge are off
0.5s
Python
# Bode plot example
# Transfer function
def H(w):
    wc = 4000*np.pi
    return 1.0 / (1.0 + 1j * w / wc)
freq = np.logspace(1,5) # frequencies from 10**1 to 10**5 Hz
plt.figure()
plt.plot(freq, 20*np.log10(abs(H(2*np.pi*freq))))
plt.xscale('log')
plt.xlabel('Frequency (Hz)')
plt.ylabel('dB')
0.9s
Python

Multiple subplots

One could use MATLAB-style to define the subplots.

But the object-oriented way is even better. See subplots().

# MATLAB style
# subplot(rows, columns, panel number)
plt.subplot(2, 1, 1)
plt.plot(x, y1)
# create the second panel and set current axis
plt.subplot(2, 1, 2)
plt.plot(x, y2)
0.5s
Python
# OO style (recommended)
fig, ax = plt.subplots(2)
# Plot for each axes (an unit in the figure)
ax[0].plot(x, y1)
ax[0].set_title("Upper panel")
ax[1].plot(x, y2)
ax[1].set_title("Lower panel")
# Common title
plt.suptitle("Common title")
0.5s
Python

Scatter plots

plt.plot(x, y, 'o')

Ref: Python Data Science Handbook

# Using plot() function
plt.figure()
x = np.linspace(0, 10)
y1 = np.sin(x)
plt.plot(x, y1, 'o', color='black')
# Same as plt.scatter(x, y1, marker='o', color='black')
0.3s
Python

Color map (cmap) and colorbar()

plt.scatter(x, y, c=colors)
plt.colorbar()

See also colormaps and colorbar

# Data #
rng = np.random.RandomState(0)
x = rng.randn(100)
y = rng.randn(100)
colors = rng.rand(100)
sizes = 1000 * rng.rand(100)
# Plot #
plt.figure()
# cmap for color mapping
plt.scatter(x, y, c=colors, s=sizes, alpha=0.3, cmap='viridis')
# show color scale bar
plt.colorbar()
0.4s
Python

Error bar

plt.errorbar(x, y, yerr=dy, fmt='.k')

See also: errorbar

# Data #
x = np.linspace(0, 10, 50)  # Input
dy = 0.8                                  # Uncertainty level
y = np.sin(x) + dy * np.random.randn(50)  # Output with uncertainty
# Plot #
plt.figure()
# xerr or yerr parameter to set error bars
plt.errorbar(x, y, yerr=dy, fmt='.k')
0.8s
Python

Contour plots

plt.contour(X, Y, Z)

See also contour() and imshow()

# data #
def f(x, y):
    return np.sin(x) ** 10 + np.cos(10 + y * x) * np.cos(x)
x = np.linspace(0, 5, 50)
y = np.linspace(0, 5, 40)
X, Y = np.meshgrid(x, y)
Z = f(X, Y)
# plot #
plt.figure()
plt.contour(X, Y, Z)
0.4s
Python
plt.figure()
# Change color map
plt.contour(X, Y, Z, 20, cmap='RdGy')
0.4s
Python
plt.figure()
# contourf() for filled countor plot
plt.contourf(X, Y, Z, 20, cmap='RdGy')
plt.colorbar()
0.4s
Python
plt.figure()
contours = plt.contour(X, Y, Z, 3, colors='black')
# Add labels of levels in the contour plot
plt.clabel(contours, inline=True, fontsize=8)
# Render image on the plot (faster but lower quality)
plt.imshow(Z, extent=[0, 5, 0, 5], origin='lower', cmap='RdGy', alpha=0.5)
plt.colorbar()
0.7s
Python
#### set_clim() to set limits on the values in the color bar
import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl
# Data #
x = np.linspace(0, 10, 1000)	            # 1000 * 1
I = np.sin(x) * np.cos(x[:, np.newaxis])  # 1000 * 1000
speckles = (np.random.random(I.shape) < 0.01)
I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles))
# Figure #
fig, axs = plt.subplots(ncols=2, figsize=(10, 5))
# Left subplot
axs[0].set_title('Without limit')
im0 = axs[0].imshow(I, cmap='RdBu')
cb0 = plt.colorbar(im0, ax=axs[0], orientation='horizontal')
# Right subplot
axs[1].set_title('With limit')
im1 = axs[1].imshow(I, cmap='RdBu')
im1.set_clim(-1, 1)
cb1 = plt.colorbar(im1, ax=axs[1], extend='both', orientation='horizontal')
1.3s
Python

Plotting vector fields (quiver / streamplot plot)

Source: https://scipython.com/blog/visualizing-the-earths-magnetic-field/

More on: quiver(), streamplot()

Another example: https://stackoverflow.com/questions/25342072/computing-and-drawing-vector-fields

import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Circle
# Mean magnitude of the Earth's magnetic field at the equator in T
B0 = 3.12e-5
# Radius of Earth, Mm (10^6 m: mega-metres!)
RE = 6.370
# Deviation of magnetic pole from axis
alpha = np.radians(9.6)
def B(r, theta):
    """Return the magnetic field vector at (r, theta)."""
    fac = B0 * (RE / r)**3
    return -2 * fac * np.cos(theta + alpha), -fac * np.sin(theta + alpha)
# Grid of x, y points on a Cartesian grid
nx, ny = 64, 64
XMAX, YMAX = 40, 40
x = np.linspace(-XMAX, XMAX, nx)
y = np.linspace(-YMAX, YMAX, ny)
X, Y = np.meshgrid(x, y)
r, theta = np.hypot(X, Y), np.arctan2(Y, X)
# Magnetic field vector, B = (Ex, Ey), as separate components
Br, Btheta = B(r, theta)
# Transform to Cartesian coordinates: NB make North point up, not to the right.
c, s = np.cos(np.pi/2 + theta), np.sin(np.pi/2 + theta)
Bx = -Btheta * s + Br * c
By = Btheta * c + Br * s
fig, ax = plt.subplots()
# Plot the streamlines with an appropriate colormap and arrow style
color = 2 * np.log(np.hypot(Bx, By))
ax.streamplot(x, y, Bx, By, color=color, linewidth=1, cmap=plt.cm.inferno,
              density=2, arrowstyle='->', arrowsize=1.5)
# Add a filled circle for the Earth; make sure it's on top of the streamlines.
ax.add_patch(Circle((0,0), RE, color='b', zorder=100))
ax.set_xlabel('$x$')
ax.set_ylabel('$y$')
ax.set_xlim(-XMAX, XMAX)
ax.set_ylim(-YMAX, YMAX)
ax.set_aspect('equal')
2.1s
Python
# Another vector field example
import numpy as np
import matplotlib.pyplot as plt
# Set limits and number of points in grid
y, x = np.mgrid[10:-10:100j, 10:-10:100j]
x_obstacle, y_obstacle = 0.0, 0.0
alpha_obstacle, a_obstacle, b_obstacle = 1.0, 1e3, 2e3
p = -alpha_obstacle * np.exp(-((x - x_obstacle)**2 / a_obstacle + (y - y_obstacle)**2 / b_obstacle))
# For the absolute values of "dx" and "dy" to mean anything, we'll need to
# specify the "cellsize" of our grid.  For purely visual purposes, though,
# we could get away with just "dy, dx = np.gradient(p)".
dy, dx = np.gradient(p)
fig, ax = plt.subplots()
ax.quiver(x, y, dx, dy, p)
ax.set(aspect=1, title='Quiver Plot')
0.6s
Python
# Quiver plot with less density (draw every 3rd point)
skip = (slice(None, None, 3), slice(None, None, 3))
fig, ax = plt.subplots()
ax.quiver(x[skip], y[skip], dx[skip], dy[skip], p[skip])
ax.set(aspect=1, title='Quiver Plot (3rd points)')
0.9s
Python
# Streamplot with contour plot
fig, ax = plt.subplots()
ax.streamplot(x, y, dx, dy, color=p, density=0.5, cmap='gist_earth')
cont = ax.contour(x, y, p, cmap='gist_earth')
ax.clabel(cont)
ax.set(aspect=1, title='Streamplot with contours')
0.8s
Python
# Streamplot with stroke + contour plot
from matplotlib.patheffects import withStroke
fig, ax = plt.subplots()
ax.streamplot(x, y, dx, dy, linewidth=500*np.hypot(dx, dy),color=p, density=1.2, cmap='gist_earth')
cont = ax.contour(x, y, p, cmap='gist_earth', vmin=p.min(), vmax=p.max())
labels = ax.clabel(cont)
plt.setp(labels, path_effects=[withStroke(linewidth=8, foreground='w')])
ax.set(aspect=1, title='Streamplot with contours and strokes')
1.5s
Python

anotations

anotations: https://matplotlib.org/tutorials/text/annotations.html#plotting-guide-annotation

data = np.random.rand(10)
plt.plot(data)
plt.annotate("Text",(2,0.5),(1,0.2),arrowprops= dict())
plt.annotate("peak",
             (np.where(data==data.max())[0][0],data.max()), # where to point
             xycoords='data', 
             xytext=(np.where(data==data.max())[0][0]+1,data.max()-0.1), # where to put text
             arrowprops = dict(facecolor="grey",shrink=0.09)) # arrow property
plt.annotate("fixed arrow",
             (0.8,0.8),xycoords='axes fraction',
             xytext=(0.5,0.5),textcoords='axes fraction',
             arrowprops = dict(arrowstyle="->")
            )
# plt.show()
0.7s
Python
Runtimes (1)