Matplotlib Review
I wanted to review Matplotlib before continuing learning about Machine Learning. There were some code snippets in Hands On Machine Learning that I didn't understand well do to my lack of knowledge about Matplotlib.
References
- Matplotlib Documentation
- Embedding Matplotlib in Graphical User Interfaces
- Allowable Color Definitions
- Text in Matplotlib
Quick Start Guide
Matplotlib graphs your data on Figures, each of which can contain one or more Axes, an area where points can be specified in terms of x-y coordinates (or (θ,r) in polar plot, or (x,y,z) in 3D plot, etc.). The simplest way of creating a Figure on an Exes is by using pyplot.subplot. We can then use Axes.plot to draw some data on the Axes, and show to display the figure:
import matplotlib.pyplot as plt
ax = plt.subplot() # Create a figure containing a single Axes
ax.plot([1,2,3,4],[1,4,2,3]) # Plot some data on the Axes
plt.show() # Show the figure
Anatomy of a Matplotlib Figure
Figure
The whole figure. The Figure keeps track of all the child Axes, a group of 'special' Artists (titles, figure legends, colorbars, etc.) and even nested subfigures. Typically, you'll create a new Figure through one of the following functions:
fig = plt.figure() # An empty figure with no Axes
fig, ax = plt.subplots() # a figure with a single axes
fig, axs = plt.subplots(2,2) # a figure with a 2x2 grid of Axes
# A figure with one axes on the left, and two on the right
fig, axs = plt.subplot_mosaic([["left","right_top"],["left","right_bottom"]])
Axes
- An Axes is an Artist attached to a Figure that contains a region for plotting data, and usually includes two (or three in the case of 3D) Axis objects that provide ticks and tick labels to provide scales for the data in the Axes. Each Axes also has a title (set via set_title()), an x-label (set via set_xlabel()) and a y-label (set via set_ylabel()).
- The Axes methods are the primary interface for configuring most parts of your plot (adding data, controlling axis-scales and limits, adding labels, etc.)
Axis
These objects set the scale and limits and generate ticks (the marks on the Axis) and ticklabels (strings labeling the ticks). The location of the ticks is determined by a Locator object and the ticklabel strings are formatted by a Formatter. The combination of the correct Locator and Formatter gives very fine control over the tick locations and labels.
Artist
Basically, everything visible on the Figure is an Artist (even Figure, Axes, and Axis objects). This includes Text objects, Line2D objects, collections objects, Patch objects, etc. When the Figure is rendered, all of the Artists are drawn to the canvas. Most Artists are tied to an Axe; such an Artist cannot be shared by multiple Axes, or moved from one to another.
Types of Plotting functions
- Plotting functions expect numpy.array or nump.ma.masked_array as inputs, or objects that can be passed to numpy.asarray.
- Most methods will also parse a string-indexable object like a dict, a structured numpy array, or a pd.DataFrame. Matplotlib allows you to provide the data keyword argument and generate plots passing the strings corresponding to the x and y variables.
import numpy as np
np.random.seed(19680801) # seed the random number generator.
data = {'a': np.arange(50),
'c': np.random.randint(0, 50, 50),
'd': np.random.randn(50)}
data['b'] = data['a'] + 10 * np.random.randn(50)
data['d'] = np.abs(data['d']) * 100
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.scatter('a', 'b', c='c', s='d', data=data)
ax.set_xlabel('entry a')
ax.set_ylabel('entry b')
Coding Styles
- There are essentially two ways to use Matplotlib:
- Explicitly create Figures and Axes, and call methods on them (the object-oriented style)
- Rely on pyplot to implicitly create and manage the Figures and Axes, and use pyplot functions for plotting.
- Embedding Matplotlib in Graphical User Interfaces
In general, they suggest the OO style, particularly for complicated plots, and functions and scripts that are intended to be reused as a part of a larger project. The pyplot style can be very convenient for quick interactive work. Matplotlib suggests making helper functions if you intend to create the same kind of graph over and over again
x = np.linspace(0,2,100) # sample data
## Object Oriented Style
# Note that even in the OO-style, we use `.pyplot.figure` to create the Figure.
fig, ax = plt.subplots(figsize=(5, 2.7), layout='constrained')
ax.plot(x, x, label='linear') # Plot some data on the Axes.
ax.plot(x, x**2, label='quadratic') # Plot more data on the Axes...
ax.plot(x, x**3, label='cubic') # ... and some more.
ax.set_xlabel('x label') # Add an x-label to the Axes.
ax.set_ylabel('y label') # Add a y-label to the Axes.
ax.set_title("Simple Plot") # Add a title to the Axes.
ax.legend() # Add a legend.
## Pyplot Style
plt.figure(figsize=(5, 2.7), layout='constrained')
plt.plot(x, x, label='linear') # Plot some data on the (implicit) Axes.
plt.plot(x, x**2, label='quadratic') # etc.
plt.plot(x, x**3, label='cubic')
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()
Styling Artists
Most plotting methods have styling options for the Artists, accessible either when a plotting method is called, or from a setter on the Artist. In the plot below, we manually set the color, linewidth, and linestyle of the Artist created by plot, and we set the linestyle of the second line after the fact with set_linestyle.
data1, data2, data3, data4 = np.random.randn(4, 100) # make 4 random data sets
fig, ax = plt.subplots(figsize=(5, 2.7))
x = np.arange(len(data1))
ax.plot(x, np.cumsum(data1), color='blue', linewidth=3, linestyle='--')
l, = ax.plot(x, np.cumsum(data2), color='orange', linewidth=2)
l.set_linestyle(':')
Colors
Matplotlib has a very flexible array of colors that are accepted for most Artists. Some Artists will take multiple colors - for a scatter plot you can define the fill and border color for a marker.
Color Formats
- RGB or RGBA Values as a Tuple of Floats
- (0.1, 0.2, 0.5)
- (0.2, 0.2, 0.5, 0.3)
- Case-insensitive hex RGB or RGBA values
- '#0f0f0f'
- '#0f0f0f80'
- '#abc'
- String representative of float value in closed interval [0,1] for grayscale value
- '0' as black
- '1' as white
- '0.8' as light gray
- String character shorthand
- 'b' as blue
- 'g' as green
- 'r' as red
- 'c' as cyan
- 'm' as magenta
- 'y' as yellow
- 'k' as black
- 'w' as white
- Case insensitive CSS4 color name
- aquamarine
- mediumseagreen
- Tuple of one of tha above color formats with an alpha float
- ('grren',0.3)
- ('#f00',0.9)
Common Marker Styles
- o = circle
- d = diamond
- v = inverted triangle
- s = square
Labelling Plots
set_xlabel, set_ylabel, and set_title are used to add text in the indicated locations. Test can also be added to plots using text. You can include LaTeX expressions in text by using a raw string and wrapping the TeX expression in $.
Axis Scales and Ticks
In addition to the linear scale, Matplotlib supplies non-linear scales, such as log-scale. Since log-scales are used s much there are also direct methods like loglog, semilogx, and semilogy. There are a number of scales. Each Axis has a tick locator and formatter that choose where along the Axis objects to put tick marks. A simple interface is set_xyticks and sety_yticks. Matplotlib can handle plotting arrays of dates and arrayus of strings, as well as floating point numbers. These get special locators and formatters as appropriate.
Plotting data of different magnitude in one chart may require an additional y-axis. Such an axis can be created by using twinx to add a new Axes with an invisible x-axis and a y-axis positioned at the right (analagously twiny)
Color Mapped Data
Often we have a third dimension in a plot represented by colors in a color map. Matplotlib has a number of plot types that do this. Matplotlib has many color maps to choose from.. Adding a colorbar gives a key to relate the color back to the underlying data.
Working with Multiple Figures and Axes
You can open multiple figures with multiple calls to fig = plt.figure() or fig2, ax = plt.subplots(). By keeping the object references you can add Artists to either figure.
import numpy as np
import matplotlib.pyplot as plt
data1, data2, data3, data4 = np.random.randn(4, 100) # make 4 random data sets
mu, sigma = 115, 15
x = mu + sigma*np.random.randn(10000)
fig, ax = plt.subplots(figsize=(5,2.7),layout="constrained")
# histogram of the data
n, bins, patches = ax.hist(x, 50, density=True, facecolor='C0', alpha=0.75)
ax.set_xlabel("Length [cm]",color="red",fontsize=14)
ax.set_ylabel("Probability")
ax.set_title("Arvaarj Lengths \n (not really)")
ax.text(75,0.025,r'$ \mu=115,\ \sigma=15 $')
ax.axis([55,175,0,0.03])
ax.grid(True)
plt.show()
fig, ax = plt.subplots(figsize=(5,2.7))
t = np.arange(0.0,5.0,0.01)
s = np.cos(2 * np.pi * t)
line, = ax.plot(t,s,lw=2)
ax.annotate('local max', xy=(2,1), xytext=(3,1.5), arrowprops=dict(facecolor="red", shrink=0.05))
ax.set_ylim(-2,2)
plt.show()
## Additional Axis
fig, (ax1, ax3) = plt.subplots(1, 2, figsize=(7, 2.7), layout='constrained')
l1, = ax1.plot(t, s)
ax2 = ax1.twinx()
l2, = ax2.plot(t, range(len(t)), 'C1')
ax2.legend([l1, l2], ['Sine (left)', 'Straight (right)'])
ax3.plot(t, s)
ax3.set_xlabel('Angle [rad]')
ax4 = ax3.secondary_xaxis('top', functions=(np.rad2deg, np.deg2rad))
ax4.set_xlabel('Angle [°]')
## Color Mapped Data
from matplotlib.colors import LogNorm
X, Y = np.meshgrid(np.linspace(-3, 3, 128), np.linspace(-3, 3, 128))
Z = (1 - X/2 + X**5 + Y**3) * np.exp(-X**2 - Y**2)
fig, axs = plt.subplots(2, 2, layout='constrained')
pc = axs[0, 0].pcolormesh(X, Y, Z, vmin=-1, vmax=1, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[0, 0])
axs[0, 0].set_title('pcolormesh()')
co = axs[0, 1].contourf(X, Y, Z, levels=np.linspace(-1.25, 1.25, 11))
fig.colorbar(co, ax=axs[0, 1])
axs[0, 1].set_title('contourf()')
pc = axs[1, 0].imshow(Z**2 * 100, cmap='plasma', norm=LogNorm(vmin=0.01, vmax=100))
fig.colorbar(pc, ax=axs[1, 0], extend='both')
axs[1, 0].set_title('imshow() with LogNorm()')
pc = axs[1, 1].scatter(data1, data2, c=data3, cmap='RdBu_r')
fig.colorbar(pc, ax=axs[1, 1], extend='both')
axs[1, 1].set_title('scatter()')
# Figures and Axes
fig, axd = plt.subplot_mosaic([['upleft', 'right'],
['lowleft', 'right']], layout='constrained')
axd['upleft'].set_title('upleft')
axd['lowleft'].set_title('lowleft')
axd['right'].set_title('right')
Figures and Backends
When looking at Matplotlib, you are almost always looking at Artists placed on a Figure. In the example below, the digure is the blue region and the add_subplot has added an Axes artist to the Figure. The most common way to create a figure is using the pyplot interface. Sometimes we have a nested layout in a figure, with two or more sets of Axes that do not share the same subploy grid. You can use add_subfigure or subfigures to create virtual figures inside parent figures.
Figure Options
There are a few options available when creating figures:
- figsize is the (width, height) of the Figure in inches
- faceolor, edgecolor, linewidth, and frameon options all change the appearance of teh figure in expected ways
- layout can be "constrained", "compressed", or "ight". These rescale axes inside the figure to prevent overlap of ticklabels, and try and align teh axes, and can save significant manual adjustment of artists on a Figure for many common cases.
Adding Artists
The Figure class has a number of methods fopr adding artists to a Figure or SubFigure. By far the most common are to add Axes of various configurations (add_axes, add_subplots, subplots, subplot_mosaic) and subfigures (subfigures). Colorbars are also added at the figure level (colorbar). You can have a figure level legend (legend). Other Artists include figure-side labels (suptitle, supxlabel, supylabel) and text (text).
Yoou can save a figure with the savefig method.
fig = plt.figure(figsize=(2, 2), facecolor='lightskyblue',
layout='constrained')
fig.suptitle('Figure')
ax = fig.add_subplot()
ax.set_title('Axes', loc='left', fontstyle='oblique', fontsize='medium')
## The most common way to create figures
fig, axs = plt.subplots(2, 2, figsize=(4, 3), layout='constrained')
# More complex grids can be achieved with `pyplot.subplot_mosaic`
fig, axs = plt.subplot_mosaic([['A', 'right'], ['B', 'right']],
figsize=(4, 3), layout='constrained')
for ax_name, ax in axs.items():
ax.text(0.5, 0.5, ax_name, ha='center', va='center')
# Nested Subplots
fig = plt.figure(layout='constrained', facecolor='lightskyblue')
fig.suptitle('Figure')
figL, figR = fig.subfigures(1, 2)
figL.set_facecolor('thistle')
axL = figL.subplots(2, 1, sharex=True)
axL[1].set_xlabel('x [m]')
figL.suptitle('Left subfigure')
figR.set_facecolor('paleturquoise')
axR = figR.subplots(1, 2, sharey=True)
axR[0].set_title('Axes 1')
figR.suptitle('Right subfigure')
Backends
Backends are used for displaying Matplotlib figures on the screen and for writing to files.
Matplotlib Application Interfaces
- Matplotlib Application Interfaces
- Matplotlib has two major application interfaces, or styles of using the library:
- An explicit "Axes" interface that uses methods on a Figure or Axes object to create other Artists, and build a visualization step by step. This has also been called an object-oriented interface.
- An implicit "pyplot" interface to keep track of the last Figure and Axes created, and adds Artists to the object it thinks the user wants.
In addition, a number of downstream libraries (like pandas and xarray) offer a plot method implemented directly on their data classes so that users can call data.plot().
The Explicit "Axes" Interface
The "Axes" interface is how Matplotlib is implemented, and many customizations and fine-tuning end up being done at this level. This is called the "explicit" interface because each object is explicitly referenced, and used to make the next object.
The pyplot interface
The pyplot module shows most of the Axes plotting methods to give the equivalent of the above, where the creation of the Figure and Axes is done for the user. This can be convenient. A reference to the current Figure can be retrieved using plt.gcf() and a reference to the current Axes can be gotten with plt.gca().
Third-Party plot Interfaces
Usually they pass back the Axes from the plot() method so that you can customize. You could also just use gca() or gcf() as long as they library does not call show()
import matplotlib.pyplot as plt
# Explicit Axes Interfcae
fig = plt.figure()
ax = fig.subplots()
ax.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2],c="b",marker="+",label="Explicit Axes",markersize="25")
# pyplot interface
plt.plot([1, 2, 3, 4], [0, 0.5, 1, 0.2],"ro",label="pyplot Interface",markersize=14)
plt.legend()
plt.show()
Introduction to Axes (or Subplots)
Matplotlib Axes are the gateway to creating your data visualizations. Once an Axes is placed on a figure there are many methods that can be used to add data to the Axes. An Axes typically has a pair of Axis Artists that define the data coordinate system, and include methods to add annotations like x- and y-labels, titles, and legends.
Axes are added using methods on Figure objects, or via the pyplot interface. add_axes will manually add axes on the page. plt.subplot_mosaic can be used to add axes: fig, axs = plt.subplot_mosaic([['left', 'right'], ['bottom', 'bottom']]).
Axes Plotting Methods
- Pairwise Data
- plot
- scatter
- bar
- step
- Array Objects
- pcolormesh
- contour
- quiver
- streamplot
- imshow
- Statistical Distributions
- hist
- errorbar
- hist2d
- pie
- boxplot
- violinplot
- Irregularly Gridded Data
- tricontour
- tripcolor
Axes Labelling and Annotation
The Axes.set_xlabel, Axes.sety_ylabel, and Axes.set_title can be used to add labels. The Axes.legend() method can be used to add a legend. Text can also be added to the axes using text and annotate.
Axes Limits, Scales, and Ticking
Each Axes has two or more Axis objects, that can be accessed via xaxis and yaxis properties. These have a substantial number of methods on them, but the Axes class offers a number of helpers for the most common of these methods. Axes.set_xlim and Axes.set_ylim are examples of this. The Axes calss has helpers to deal with Axis ticks and their labels. Most straight-forward is set_xticks and set_yticks which manually set the tick locations and optionally their labels. Minorticks can be adjusted using minrorticks_on or minorticks_off. Manu aspacts of Axes ticks and tick labels can be adusted using tick_params. Sometimes it is important to set the aspect ratio of a plot in data space, which we can do with set_aspect.
import matplotlib.pyplot as plt
import numpy as np
# axs is 2, 2 array of Axes
fig, axs = plt.subplots(ncols=2, nrows=2, figsize=(3.5, 2.5),
layout="constrained")
# for each Axes, add an artist, in this case a nice label in the middle...
for row in range(2):
for col in range(2):
axs[row, col].annotate(f'axs[{row}, {col}]', (0.5, 0.5),
transform=axs[row, col].transAxes,
ha='center', va='center', fontsize=18,
color='darkgrey')
fig.suptitle('plt.subplots()')
plt.show()
## Axes Plotting Methods
fig, ax = plt.subplots(figsize=(4, 3))
np.random.seed(19680801)
t = np.arange(100)
x = np.cumsum(np.random.randn(100))
lines = ax.plot(t, x)
plt.show()
fig, ax = plt.subplots(figsize=(4, 2.5), layout='constrained')
np.random.seed(19680801)
t = np.arange(200)
x = 2**np.cumsum(np.random.randn(200))
linesx = ax.plot(t, x)
ax.set_yscale('log')
ax.set_xlim([20, 180])
plt.show()
fig, ax = plt.subplots(figsize=(4, 2.5))
ax.plot(np.arange(10))
ax.tick_params(top=True, labeltop=True, color='red', axis='x',
labelcolor='green')
fig, axs = plt.subplots(ncols=2, figsize=(7, 2.5), layout='constrained')
np.random.seed(19680801)
t = np.arange(200)
x = np.cumsum(np.random.randn(200))
axs[0].plot(t, x)
axs[0].set_title('aspect="auto"')
axs[1].plot(t, x)
axs[1].set_aspect(3)
axs[1].set_title('aspect=3')
plt.show()
Adding Multiple Axes in a Figure
Often more than one Axes is wanted on a figure at a time, usually organized into a regular grid. Matplotlib has a variety of tools for working with grids of Axes that have evolved over the history of the library.
def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):
return np.sin(i*a)*np.cos(i*b), np.sin(i*c)*np.cos(i*d)
fig = plt.figure(figsize=(8, 8), layout='constrained')
outer_grid = fig.add_gridspec(4, 4, wspace=0, hspace=0)
for a in range(4):
for b in range(4):
# gridspec inside gridspec
inner_grid = outer_grid[a, b].subgridspec(3, 3, wspace=0, hspace=0)
axs = inner_grid.subplots() # Create all subplots for the inner grid.
for (c, d), ax in np.ndenumerate(axs):
ax.plot(*squiggle_xy(a + 1, b + 1, c + 1, d + 1))
ax.set(xticks=[], yticks=[])
# show only the outside spines
for ax in fig.get_axes():
ss = ax.get_subplotspec()
ax.spines.top.set_visible(ss.is_first_row())
ax.spines.bottom.set_visible(ss.is_last_row())
ax.spines.left.set_visible(ss.is_first_col())
ax.spines.right.set_visible(ss.is_last_col())
plt.show()
Placing Colorbars
Colorbars indicate the quantitative extent of image data. Placing in a figure is non-trivial because room needs to be made for them. The simplest case is just attaching a colorbar to each Axes. The 'constrained' layout should probably be used when using colorbars.
Margins
The margins around the data in the plot can be set with Axes.margins() method.
Axis Scales
By default Matplotlib displays data on the axis using a linear scale. Matplotlib also supports logarithmic scales, and other less common scales as well. Usually this can be done directly by using the Axes.set_xscale or Axes.set_yscale methods. To options for scale names can be found in matplotlib.scale.get_scale_names()
import matplotlib.pyplot as plt
import numpy as np
# Fixing random state for reproducibility
np.random.seed(19680801)
fig, axs = plt.subplots(2, 2)
cmaps = ['RdBu_r', 'viridis']
for col in range(2):
for row in range(2):
ax = axs[row, col]
pcm = ax.pcolormesh(np.random.random((20, 20)) * (col + 1),
cmap=cmaps[col])
fig.colorbar(pcm, ax=ax)
Axis Ticks
The x and y Axis have default tick "locators" and "formatters" that depend on the scale being used. It is possible to customize tick locations and formats using set_xticks() and set_yticks()
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig, axs = plt.subplots(2, 1, figsize=(5.4, 5.4), layout='constrained')
x = np.arange(100)
for nn, ax in enumerate(axs):
ax.plot(x, x)
if nn == 1:
ax.set_title('Manual ticks')
ax.set_yticks(np.arange(0, 100.1, 100/3))
xticks = np.arange(0.50, 101, 20)
xlabels = [f'\\${x:1.2f}' for x in xticks]
ax.set_xticks(xticks, labels=xlabels)
else:
ax.set_title('Automatic ticks')
fig, axs = plt.subplots(1, 2, figsize=(6.4, 3.2), layout='constrained')
for nn, ax in enumerate(axs):
ax.plot(np.arange(100))
if nn == 1:
ax.grid('on')
ax.tick_params(right=True, left=False, axis='y', color='r', length=16,
grid_color='none')
ax.tick_params(axis='x', color='m', length=4, direction='in', width=4,
labelcolor='g', grid_color='b')
Legend Guide
Just use help(plt.Axes.legend) to learn more about most things in the library.
Introduction to Artists
Almost all objects you interact with on a Matplotlib plot are called "Artist" (and are subclasses of the Artist class). Figure and Axes are Artists, and generally contain Axis Artists and Artists that contain data or annotation information. Ususually, we do not instantiate Artists directly, but rather use a plotting method on Axes. Some examples of plotting methods and the artist object:
fig, ax = plt.subplots(figsize=(4, 2.5))
x = np.arange(0, 13, 0.2)
y = np.sin(x)
lines = ax.plot(x, y, '-', label='example', linewidth=0.2, color='blue')
lines[0].set(color='green', linewidth=2)
import matplotlib.artist as Artist
# Interogate the full width of settable properties
print(Artist.getp(lines[0]))
Text
Matplotlib has extensive text support, including support for mathematical expressions, truetype support for raster and vector outputs, newline separated text with arbitrary rotations, and Unicode support.
import matplotlib.pyplot as plt
import matplotlib
fig = plt.figure()
ax = fig.add_subplot()
fig.subplots_adjust(top=0.85)
# Set titles for the figure and the subplot respectively
fig.suptitle('bold figure suptitle', fontsize=14, fontweight='bold')
ax.set_title('axes title')
ax.set_xlabel('xlabel')
ax.set_ylabel('ylabel')
# Set both x- and y-axis limits to [0, 10] instead of default [0, 1]
ax.axis([0, 10, 0, 10])
ax.text(3, 8, 'boxed italics text in data coords', style='italic',
bbox={'facecolor': 'red', 'alpha': 0.5, 'pad': 10})
ax.text(2, 6, r'an equation: $E=mc^2$', fontsize=15)
ax.text(3, 2, 'Unicode: Institut für Festkörperphysik')
ax.text(0.95, 0.01, 'colored text in axes coords',
verticalalignment='bottom', horizontalalignment='right',
transform=ax.transAxes,
color='green', fontsize=15)
ax.plot([2], [1], 'o')
ax.annotate('annotate', xy=(2, 1), xytext=(3, 4),
arrowprops=dict(facecolor='black', shrink=0.05))
plt.show()