/python-plotting-tutorial

Short tutorial on python plotting

Primary LanguagePython

python-plotting-tutorial

Short tutorial (1.5h) on python plotting with matplotlib

Contents of tutorial

matplotlib 3.0.2

About matplotlib

The project was initiated by John D. Hunter in 2008. The initial introductory text stated:

"Matplotlib is a library for making 2D plots of arrays in Python. Although it has its origins in emulating the MATLAB graphics commands, it is independent of MATLAB, and can be used in a Pythonic, object oriented way. Although Matplotlib is written primarily in pure Python, it makes heavy use of NumPy and other extension code to provide good performance even for large arrays."

To keep in mind: "Matplotlib tries to make easy things easy and hard things possible". The matplotlib "easy" part is named pyplot module and provides a MATLAB-like interface.

Example 1: simple basic plot

We start with this basic example of plotting 1+sin(x) on range 0..2:

import matplotlib
# matplotlib.use('Agg') # use this backend if you would just like to save images/figures to files (PNG, JPG, PDF)
import matplotlib.pyplot as plt
import numpy as np

x = np.arange(0.0, 2.0, 0.01) # data for plotting
y = 1 + np.sin(x)

fig, ax = plt.subplots() # get figure in fig and axis in ax object
ax.plot(x, y)

ax.set(xlabel='time (s)', ylabel='1+sin(x)', title='basic matplotlib example') # set xlabel, ylabel and title
ax.grid()

fig.savefig("example1.png") # save figure
plt.show() # show figure

Notice the figure is saved to example1.png using .savefig() and displayed with show().

Exercise 1

Modify the code from Example 1 to plot tan(x) on the range of -10 .. 10, with the step of 0.1. Your points will be -10, -9.9, -9.8 .. 9.9, 10. Also, don't plot a continuous line (plot), but only plot points (scatter). Color the points red (#FF0000). Save figure to PDF file.

Solution
import matplotlib
import matplotlib.pyplot as plt
import numpy as np

t = np.arange(-10, 10.0, 0.1) # data for plotting
s = np.tan(t)

fig, ax = plt.subplots()
ax.scatter(t, s, c="#FF0000") # c="red" would also work

ax.set(xlabel='x range', ylabel='tan(x)', title='tan(x) on range -10..10 [step 0.1]')
ax.grid()

fig.savefig("exercise1.pdf") # save to PDF simply by changing filename extension (.pdf)
plt.show()

Example 2: histogram

A slightly more complex example with a histogram:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)

mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(5000)
num_bins = 50

fig, ax = plt.subplots()
n, bins, patches = ax.hist(x, num_bins, density=1) # plot histogram

# add 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('histogram bins')
ax.set_ylabel('probability density')
ax.set_title(r'histogram: $\mu=100$, $\sigma=15$')

fig.tight_layout() # make display nicer
plt.show()

Exercise 2

Let's try to style this plot and make it more compact. With help of matplotlib documentation do the following:

  • change font size of axis labels and title to 8px, use commands like matplotlib.rcParams['axes.labelsize']=8; try to find out other properties (axes.titlesize etc.)
  • display the grid and make it more transparent (alpha=0.2), check Example 1 to see how to display the grid
  • save the plot to a SVG file
  • try out tight_layout()
Solution
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
np.random.seed(42)

font_size = 8
matplotlib.rcParams['axes.labelsize'] = font_size
matplotlib.rcParams['axes.titlesize'] = font_size
matplotlib.rcParams['xtick.labelsize'] = font_size
matplotlib.rcParams['ytick.labelsize'] = font_size
matplotlib.rcParams['legend.fontsize'] = font_size
matplotlib.rc('axes', edgecolor='gray')
matplotlib.rcParams['axes.linewidth'] = 0.2
matplotlib.rcParams['legend.frameon'] = 'False'
import matplotlib.colors as mcolors

mu = 100  # mean of distribution
sigma = 15  # standard deviation of distribution
x = mu + sigma * np.random.randn(5000)
num_bins = 50

fig, ax = plt.subplots()
n, bins, patches = ax.hist(x, num_bins, density=1) # plot histogram

# add 'best fit' line
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-0.5 * (1 / sigma * (bins - mu))**2))
ax.plot(bins, y, '--')
ax.set_xlabel('histogram bins')
ax.set_ylabel('probability density')
ax.set_title(r'histogram: $\mu=100$, $\sigma=15$')

plt.grid()
plt.grid(alpha=0.2)

fig.tight_layout() # good trick
fig.savefig("exercise2.svg")
plt.show()

Example 3: coloring areas between plots

Our last example introduces the final exercise. Imagine we have 2 plots on the same figure:

import matplotlib
import matplotlib.pyplot as plt
import numpy as np
font_size = 8
matplotlib.rcParams['axes.labelsize'] = font_size
matplotlib.rcParams['axes.titlesize'] = font_size
matplotlib.rcParams['xtick.labelsize'] = font_size
matplotlib.rcParams['ytick.labelsize'] = font_size
matplotlib.rcParams['legend.fontsize'] = font_size
matplotlib.rc('axes',edgecolor='gray')
matplotlib.rcParams['axes.linewidth'] = 0.2
matplotlib.rcParams['legend.frameon'] = 'False'

fig, ax = plt.subplots()

x = np.arange(0.0, 2.0, 0.01) # Data for plotting

y1 = 1+x+x*x
ax.plot(x, y1)
y2 = -4+5*x+x*x+x*x*x
ax.plot(x, y2)

ax.set(xlabel='x in range 0..2 by steps of 0.01', ylabel='', title='plot coloring example')
plt.grid(alpha=0.2)

plt.legend() # show legend

fig.savefig("example3.png")
plt.show()

The first plot (x, y1) is of the function 1+x+x^2, the second plot (x, y2) is of the function -4+5x+x^2+x^3. Run the code and move on to the exercise to learn additional styling and how to color areas between plots.

Exercise 3

Again using hints from this text and matplotlib documentation, modify the above Example 3 code to include:

  • add legends to the two plots (hint: use attribute legend with the plot command); finally, you have to display the legend (plt.legend)
  • make the two plots red and blue (use the attribute c of .plot); you can also use pastel colors, like #ff6961 for red and #779ecb for blue
  • make lines of the two plots thicker; use linewidth attribute of the plot method
  • color the area in-between the plots with a light gray (#dddddd or similar) color; hint: try using fill_between
  • set x-axis and y-axis limits to some reasonable value, so the labels on the sides of the axis are displayed nicely; an example could be -5..20 (y), -0.2..2.2 (x); use set_xlim and set_ylim
Solution
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
font_size = 8
matplotlib.rcParams['axes.labelsize'] = font_size
matplotlib.rcParams['axes.titlesize'] = font_size
matplotlib.rcParams['xtick.labelsize'] = font_size
matplotlib.rcParams['ytick.labelsize'] = font_size
matplotlib.rcParams['legend.fontsize'] = font_size
matplotlib.rc('axes',edgecolor='gray')
matplotlib.rcParams['axes.linewidth'] = 0.2
matplotlib.rcParams['legend.frameon'] = 'False'

fig, ax = plt.subplots()

x = np.arange(0.0, 2.0, 0.01) # data for plotting

y1 = 1+x+x*x
ax.plot(x, y1, c='#ff6961', label="1+x+x^2", linewidth=2)
y2 = -4+5*x+x*x+x*x*x
ax.plot(x, y2, c='#779ecb', label="-4+5x^2+x^3", linewidth=2)

ax.fill_between(x, y1, y2, color='#dddddd') # fill the space in-between the plots

ax.set(xlabel='x in range 0..2 by steps of 0.01', ylabel='', title='plot coloring example')
ax.set_ylim(-5, 20) # set area to display (y-axis)
ax.set_xlim(-0.2, 2.2) # set area to display (x-axis)
plt.grid(alpha=0.2)

plt.legend() # show legend

fig.savefig("example3.png")
plt.show()

Summary and take home message

Basic plotting with matplotlib is easy however fine-tuning your plots requires some additional work. Overall, matplotlib is a very powerful tool to produce high-quality plots that you can customize in great detail.

Quick start summary

  • first steps:
import matplotlib
# matplotlib.use('Agg') # non-interactive back-end
import matplotlib.pyplot as plt
import numpy as np
  • start plotting immediatelly:
fig, ax = plt.subplots()
x = np.arange(0.0, 2.0, 0.01)
y = 2*x
ax.plot(x, y)
plt.show()
  • easily style the ax.plot with several attributes
    • c="red" or c="#FF0000"
    • linewidth=2 (or any integer)
    • label="my plot label": this is then recognized by plt.legend()
  • other useful commands
    • plt.grid(alpha=1): shows the grid
    • ax.set_ylim(y_from, y_to) and ax.set_xlim(x_from, x_to): set the display limits
    • ax.fill_between(x, y1, y2, color="lightgray"): fills the area between y1 and y2 with light gray color
    • plt.tight_layout(): usuful to make the figure nicer
    • fig.savefig(filename): save figure to file, supported formats: png, jpg, svg, pdf (simply change the file extension)

Hope you enjoyed the tutorial, please open an issue if you have further comments.