Matplotlib is one of the most popular and flexible function librariesfor data visualization in use today. This crash course is meant tosummarize and compliment the official documentation, but you areencouraged to refer to the original documentation for fullerexplanations of function arguments.
Prerequisites
In order to follow this course, you will need to be familiar with:
The Python 3.X language, data structures (e.g. dictionaries), and built-in functions (e.g. string manipulation functions)
NumPy: array I/O and manipulation
It will also help to have experience with:
SciPy
Pandas
LaTeX math typesetting (reference links are provided)
Before we get started, let’s the meanings of the terms args
and kwargs
, since they will appear frequently:
args
refer to positional arguments, which are usuallymandatory, but not always. These always come before thekwargs
.kwargs
are short for keyword arguments. These are usuallyoptional, but it’s fairly common for some python functions torequire a variable subset of all available kwargs dependent onprevious inputs. These always come afterargs
.
It will also help you to remember what classes, methods, andattributes are:
classes
are templates to make Python objects. They have abuilt-in__init__()
function to set initial properties thatmust be defined when an object of this class is created, and theymethods and attributes to compute values or functions with. Once aclass is defined, you typically define an instance of it likeobj = MyClass(...)
.methods
associate functions with the class and allow quickevaluation for each class instance. For an objectobj
of classMyClass
that has methods, the method syntax looks like this:obj.MyMethod()
orobj.MyMethod(*args, **kwargs)
.attributes
let you automatically compute and store valuesthat can be derived for any instance of the class. For an objectobj
with an attributeMyAttribute
, the syntax isobj.MyAttribute
; i.e. the main difference between attributesand methods is that attributes do not take arguments.
Load and Run
If you use Matplotlib at the command line, after importing matplotlib, you will need to set matplotlib.use('Tkinter')
in your script or at the Python prompt in order to view your plots.
Alternatively, you can use a GUI, either JupyterLab or Spyder, but you will still have to pre-load Matplotlib and any other modules you want to use (if you forget any, you’ll have to close the GUI and reopen it after loading the missing modules) before loading either of them. The command to start Jupyter Lab after you load it is jupyter-lab
, and the Spyder launch command is spyder3. Unfortunately the only version of Spyder available is pretty old.
As of 27-11-2024, ml spider matplotlib
outputs the following versions:
---------------------------------------------------------------------------- matplotlib:---------------------------------------------------------------------------- Versions: matplotlib/2.2.4-Python-2.7.15 matplotlib/2.2.4-Python-2.7.16 matplotlib/2.2.4 (E) matplotlib/2.2.5-Python-2.7.18 matplotlib/2.2.5 (E) matplotlib/3.1.1-Python-3.7.4 matplotlib/3.1.1 (E) matplotlib/3.2.1-Python-3.8.2 matplotlib/3.2.1 (E) matplotlib/3.3.3 matplotlib/3.3.3 (E) matplotlib/3.4.2 matplotlib/3.4.2 (E) matplotlib/3.4.3 matplotlib/3.4.3 (E) matplotlib/3.5.2-Python-3.8.6 matplotlib/3.5.2 matplotlib/3.5.2 (E) matplotlib/3.7.0 matplotlib/3.7.0 (E) matplotlib/3.7.2 matplotlib/3.7.2 (E) matplotlib/3.8.2 matplotlib/3.8.2 (E)Names marked by a trailing (E) are extensions provided by another module.
Basic Terms and Application Programming Interface (API)
The Matplotlib documentation has a nicely standardized vocabulary forthe different components of its output graphics. For all but thesimplest plots, you will need to know what the different componentsare called and what they do so that you know how to access andmanipulate them.
Figure: the first thing you do when you create a plot is makea
Figure
instance. It’s essentially the canvas, and itcontains all other components.Axes: most plots have 1 or more sets of
Axes
, which arethe grids on which the plots are drawn, plus all text that labelsthe axes and their increments.Axis: each individual axis is its own object. This lets youcontrol the labels, increments, scaling, text format, and more.
Artist: In Python, almost everything is an object. InMatplotlib, the figure and everything on it are objects, and everyobject is an
Artist
–every axis, every data set, everyannotation, every legend, etc. This word typically only comes upin the context of functions that create more complicated plotelements, like polygons or color bars.
For everything else on a typical plot, there’s this handy graphic:|anatomy of a plot|.. |anatomy of a plot| image:: https://matplotlib.org/stable/_images/anatomy.png
fig
? ax
? What are those?
There are 2 choices of application programming interface (API,basically a standardized coding style) in Matplotlib:
Implicit API: the quick and dirty way to visualize isolateddata sets if you don’t need to fiddle with the formatting.
Explicit API (recommended): the method that gives you handlesto the figure and axes objects (typically denoted
fig
andax
/axes
, respectively) so you can adjust the formattingand/or accommodate multiple subplots.
Most people’s first attempt to plot something in matplotlib lookslike the following example of the implicit API. The user simplyimports matplotlib.pyplot
(usually as plt
) and then plugstheir data into their choice of plotting function,plt.<function>(*args,**kwargs)
.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex = np.linspace(0,2*np.pi, 50) # fake some data# Minimum working example with 2 functionsplt.plot(x,3+3*np.sin(x),'b-', x, 2+2*np.cos(x), 'r-.')plt.xlabel('x [rads]')plt.ylabel('y')plt.title('Demo Plot - Implicit API')plt.show()
The explicit API looks more like the following example. A figureand a set of axes objects are created explicitly, usually withfig,axes = plt.subplots(nrows=nrows, ncols=ncols)
, even if therewill be only 1 set of axes (in which case the nrows
and ncols
kwargs are omitted). Then the vast majority of the plotting andformatting commands are called as methods of the axes object. Noticethat most of the formatting methods now start with set_
whencalled upon an axes
object.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex = np.linspace(0,2*np.pi, 50)# Better way for later formattingfig, ax = plt.subplots()ax.plot(x,3+3*np.sin(x),'b-')#, label=r'3+3$\times$sin(x)')ax.plot(x, 2+2*np.cos(x), 'r-.')#, label=r'2+2$\times$cos(x)')#ax.legend()ax.set_xlabel('x [rads]')ax.set_ylabel('y')ax.set_title('Demo Plot - Explicit API')plt.show()
The outputs look the same above because the example was chosen towork with both APIs, but there is a lot that can be done with theexplicit API but not the implicit API. A prime example is using thesubplots function for its main purpose, which is to support andformat 2 or more separate sets of axes on the same figure.
Subplots and Subplot Mosaics
For the standard plt.subplots(nrows=nrows, ncols=ncols)
command,the shape of axes
will be 2D if both nrows
and ncols
aregiven, 1D if either nrows
or ncols
are provided but not both,and 0D (not iterable) if neither are given.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex = np.linspace(0,2*np.pi, 50)fig, axes = plt.subplots(nrows=2, sharex=True)fig.subplots_adjust(hspace=0.05) #reduces space between 2 plotsaxes[0].plot(x,3+3*np.sin(x),'b-', label=r'3+3$\times$sin(x)')axes[1].plot(x, 2+2*np.cos(x), 'r-.', label=r'2+2$\times$cos(x)')axes[1].set_xlabel('x [rads]')for ax in axes: ax.legend() ax.set_ylabel('y')axes[0].set_title('Demo Plot - Explicit API')plt.show()
There are also the plt.subplot()
and fig.add_subplot()
methods, but they require more code to put $>$1 plot on a singlefigure. Each plot much be added 1 at a time, and there can be no morethan 9 plots on one figure. The main benefit these alternativesprovide is that different coordinate projections can be set for eachsubplot in a figure with multiple subplots. The example belowdemonstrates plt.subplot()
.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex = np.linspace(0,2*np.pi, 50)# for variable projectionsfig = plt.figure(figsize=(8,4))ax1 = plt.subplot(121)#once labels are added, have to break up plt.plot()# args cannot follow kwargsax1.plot(x,3+3*np.sin(x),'b-', label=r'3+3$\times$sin(x)')ax1.plot(x, 2+2*np.cos(x), 'r-.', label=r'2+2$\times$cos(x)')ax1.set_xlabel('x [rads]')ax1.set_ylabel('y')ax1.legend()ax1.set_title('a) Cartesian projection (default)')ax2 = plt.subplot(122, projection='polar')ax2.plot(x, 3+3*np.sin(x), 'b-', x, 2+2*np.cos(x), 'r-.')ax2.set_title('b) Polar projection')fig.suptitle('Demo Plots')plt.show()
The 3-digit number in parentheses gives the position of that set ofaxes on the subplot grid: the first digit is the total number ofpanels in a row, the second digit gives the number of plots in acolumn, and the last digit is the 1-based index of that plot as itwould appear in a flattened ordered list. E.g. if a subplot grid had2 rows and 3 columns, the top row would be indexed [1,2,3], and thebottom row would be indexed [4,5,6].
The final alternative is plt.subplot_mosaic()
, which allows oneto easily set subplots to span multiple rows or columns. The lettersthat are used to identify each plot don’t really matter as long asthey are single ASCII characters in a string. The only fixedcharacter is the .
, which is used to denote gaps. The examplebelow shows the more intuitive layout, but you can also separate rowswith ;
for more compact code (no spaces!). Moreover, there is aper_subplot_kw
to which you can pass a dictionary of the plotkeys (the same ASCII characters used to denote each subplot in thelayout string) and kwargs, which is useful if you need to specify adifferent axis projection for each plot.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex = np.linspace(0,2*np.pi, 50)fig, axd = plt.subplot_mosaic( """ ABB AC. DDD """, layout="constrained", per_subplot_kw={"C": {"projection": "polar"}, ('B','D'): {'xscale':'log'}})for k, ax in axd.items(): ax.text(0.5, 0.5, k, transform=ax.transAxes, ha="center", va="center", color="b", fontsize=25)axd['B'].plot(x, 1+np.sin(x), 'r-.', label='Plot 1')axd['D'].plot(x,0.5+0.5*np.sin(x), 'c-', label='Plot 2')fig.legend(loc='outside upper right')
<matplotlib.legend.Legend at 0x7fbadc5b4260>
The above demo also includes an example of how to add text to a plot.More on that later.
Controlling the Display
At the regular terminal, Matplotlib figures will not display unlessyou a set backend that allows displays and is compatible with yourversion of python. Backends are engines for either displayingfigures or writing them to image files (see the matplotlib docs pageon backends for moredetailfor more info). For Python 3.11.5, the one that generates figurepopups when scripts are run at the command line is Tkinter
, whichyou can set by importing the top-level matplotlib
package andthen running matplotlib.use('Tkinter')
.
Jupyter. In Jupyter, the default backend is usually fine. Afterimporting matplotlib or any of its sub-modules, you typically need toadd % matplotlib inline
before you make any plots.
Spyder. In Spyder, the default setting is for figures to bedisplayed in-line at the IPython console, which is too small and notthe best use of the resources Spyder makes available. To make figuresappear in an interactive popup, go to “Preferences”, then “IPythonconsole”, click the “Graphics” tab, and switch the Backend from“Inline” to “Automatic”. These settings will be retained from sessionto session, so you only have to do it the first time you run Spyder.
Matplotlib uses a default resolution of 100 dpi and a default figuresize of 6.4” x 4.8” (16.26 x 12.19 cm) in GUIs and with the defaultbackend. The inline backend in Jupyter (what the% matplotlib inline
command sets) uses an even lower-res defaultof 80 dpi.
The
dpi
kwarg inplt.figure()
orplt.subplots()
(not aa valid kwarg inplt.subplot()
singular) lets you change thefigure resolution at runtime. For on-screen display, 100-150 dpiis fine as long as you don’t setfigsize
too big, butpublications often request 300 DPI.The
figsize = (i,j)
kwarg inplt.figure()
andplt.subplots()
also lets you adjust the figure size and aspectratio. The default unit is inches.
Saving your Data
The Matplotlib GUI has a typical save menu option (indicated by theusual floppy disc icon) that lets you set the name, file type, andlocation. To save from your code or at the command line, there are 2options:
plt.savefig(fname, *, transparent=None, dpi='figure', format=None)
is the general-purpose save function. There are other kwargs notshown here, but these are the most important. The file type can begivenformat
or inferred from an extension given infname
.The defaultdpi
is inherited fromplt.figure()
orplt.subplots()
. Iftransparent=True
, the white backgroundof a typical figure is removed so the figure can be displayed ontop of other content.plt.imsave(fname, arr, **kwargs)
is specifically for savingarrays to images. It accepts a 2D (single-channel) array with aspecified colormap and normalization, or an RGB(A) array (a stackof images in 3 color channels, or 3 color channels and an opacityarray). Generally you also have to setorigin='lower'
for theimage to be rendered right-side up.
A few common formats that Matplotlib supports include PDF, PS, EPS,PNG, and JPG/JPEG. Other desirable formats like TIFF and SVG are notsupported natively but can be used with the installation of thePillow
module. Matplotlib has a tutorial here on importingimages into arrays for use wth``pyplot.imshow()`. <https://matplotlib.org/stable/tutorials/images.html>`__
Standard Available Plot Types
These are the categories of plots that come standard with anyMatplotlib distribution:
Pairwise plots (which accept 1D arrays of x and y data to plotagainst each other),
Statistical plots (which can be pairwise or other array-likedata),
Gridded data plots (for image-like data, vector fields, andcontours),
Irregularly gridded data plots (which usually rely on some kind oftriangulation), and
Volumetric data plots.
Volumetric, polar, and other data that rely on 3D or non-cartesiangrids typically require you to specify a projection before you canchoose the right plot type. For example, for a polar plot, you could
set
fig, ax = plt.subplots(subplot_kw = {"projection": "polar"})
to set all subplots to the same projection,set
ax = plt.subplot(nrows, ncols, index, projection='polar')
to add one polar subplot to a group of subplots with differentcoordinate systems or projections, orset
ax = plt.figure().add_subplot(projection='polar')
if youonly need 1 set of axes in total.
For volumetric data, the options are similar:
fig, ax = plt.subplots(subplot_kw = {"projection": "3d"})
formultiple subplots with the same projection,ax = plt.subplot(nrows, ncols, index, projection='3d')
for one3D subplot among several with varying projections or coordinatesystems, orax = plt.figure().add_subplot(projection='3d')
for a singularplot.
For all of the following subsections on plot type categories,commands are provided with short descrptions of their behaviors andexplanations of non-obvious args and kwargs. If not all positionalargs are required, optional ones are shown in square brackets([]
). Kwargs are shown similarly to how they are in the officialdocumentation, set equal to either their default values orthemselves. Kwargs shown as equal to themselves are technicallyNone
by default, but are shown this way to indicate that theyare part of a set of which one or more kwargs are required.Only frequently used and/or tricky kwargs are shown; refer to theofficial documentation on each command for the complete list.
Colors and colormnaps. Every plotting method accepts either asingle color (the kwarg for which may be c
or color
) or acolormap (which is usually cmap
in kwargs). Matplotlib has anexcellent series of pages on how to specify colors andtransparency,how to adjust colormapnormalizations,and which colormaps to choose based on the types of data and youraudience.
Pairwise Plots
The following is a list of plain pairwise plot commands anddescriptions, including notes about common gotchas.
.plot(x1, y1, fmt1, x2, y2, fmt2, …)
or.plot(x1, y1, fmt1, label='label')
lets you specify any numberof unlabeled lines on the same plot, OR plot one line or set ofpairwise data with arbitrary format and a label..semilogx()
,.semilogy()
, and.loglog()
are wrappers for.plot()
that accept the same args and kwargs but rescale the x, y, or both axes to log scale.
.scatter(x, y, s=rcParams['lines.markersize'] ** 2, c=‘tab:blue’)
plots data as points with tunable shapes, sizes, and colors..stem(x, y[, z])
is visually similar to scatter with linesconnecting the points to a baseline (default = x-axis), andreturns a 3-tuple of the markers, stemlines, and baseline..fill_between(x, y1, y2=0, color=‘tab:blue’, alpha=1)
lets youplot 2 lines and shade between them, which is handy for, say,showing an uncertainty region around a model function. Awhere
kwarg lets you fill only areas that match 1 specific condition..bar(cat, count, bottom=0)
and.barh(cat, count, left=0)
produce verticaland horizontal bar plots, respectively..stackplot(x, ys, baseline=0)
resembles layers offill_between()
plots;x
must be 1D, butys
can be a 2D array or a dictionaryof 1D arrays..stairs(y, edges=[x[0]]+x)
is a way of rendering a stepwisefunction or histogram where each step is heighty
betweenpointsx[i]
andx[i+1]
, i.e. the arrayedges
mustalways have 1 more element thany
..step(x, y, where=‘pre’)
is superficially similar tostairs
, butx
andy
are the same length, and you canadjust how the steps are aligned with respect tox
.
Apart from .scatter()
, most of these plots are more suited formodels rather than measurements. Related plots are shown on grids soyou can see how indexed axes objects work. Note that sharex
(andsharey
) turns off tick labels for axes along the interiorboundaries of cells in the grid.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport matplotlib as mplfig, axes=plt.subplots(nrows=2,ncols=2, sharex=True)plt.subplots_adjust(hspace=0.05) #lateral spacing is adjusted with wspace kwarg#1. Line plotsx = np.linspace(0,2*np.pi, 50)axes[0,0].plot(x,1+np.sin(x),'b-', x, 2+2*np.cos(x), 'r-.')axes[0,0].set_ylabel('y')#2. scatter (line plot data with added noise, colored by amplitude)y1 = (2+2*np.cos(x))*np.random.random_sample(len(x))y2 = (1+np.sin(x))*np.random.random_sample(len(x))axes[0,1].scatter( x, y1, s=y1*20, c=y1, cmap=mpl.colormaps['plasma'], edgecolors='b')axes[0,1].scatter( x, y2, c='k', marker='+')#3. stem (more noisy line plot data)markers,stems,baseline = axes[1,0].stem( x, y1, linefmt='k-', bottom=1.0)stems.set_linewidth(0.75)markers.set_markerfacecolor('teal')axes[1,0].set_xlabel('x [rads]')axes[1,0].set_ylabel('y')#4. fill-between with the where kwarg# single command without where fills both sides the same coloraxes[1,1].fill_between( x, 1, y1, color='b', alpha=0.5, where = y1 >= 1)axes[1,1].fill_between( x, y1, 1, color='r', alpha=0.5, where = y1 < 1)axes[1,1].set_xlabel('x [rads]')plt.show()
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinerng = np.random.default_rng()grades = rng.integers(low=55, high=100, size=[4,4])subj = ['math', 'hist', 'lang', 'sci']names = ['Tom', 'Liz', 'Harry', 'Jane']gbook = dict(zip(subj,grades))fig, axes = plt.subplots(ncols=2, figsize=(8,4))axes[0].bar(names, gbook['math'],color='c', hatch=['\\', 'o', 'x', '*'])axes[0].set_ylabel('Math scores')axes[1].barh(subj, grades[:,-1], color=['c','b','m','r'])axes[1].set_xlabel("Jane's scores")
Text(0.5, 0, "Jane's scores")
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport pandas as pdwwii_spending = pd.read_csv('docs/day1/wwii-military-spending-pct-gdp.txt',delimiter='\t', index_col=0)print(wwii_spending)year = wwii_spending.index.to_numpy()fig,axes = plt.subplots(ncols=2,figsize=(9,4), width_ratios=[5,4])axes[0].stackplot(year, wwii_spending.to_numpy().T, labels=wwii_spending.columns, baseline='wiggle')axes[0].set_xlabel("Year")axes[0].set_ylabel("Military Spending (% of Total Income)")axes[0].set_ylim(top=250)axes[0].legend(loc='upper center', ncols=3)axes[1].step(year, wwii_spending['USSR'], where='pre', ls='--', color='tab:orange', label='pre')axes[1].step(year, wwii_spending['USSR'], where='post', ls='-.', color='tab:purple', label='post')axes[1].step(year, wwii_spending['USSR'], where='mid', color='tab:red', label='mid')axes[1].set_xlabel("Year")axes[1].set_ylabel("USSR Military Spending (% of Total Income)")axes[1].legend()plt.show()
US Germany UK USSR Italy JapanYear 1939 1 23 15 12 8 221940 2 40 44 17 12 221941 11 52 53 28 23 271942 31 64 52 61 22 331943 42 70 55 61 21 431944 42 75 53 53 0 76
Statistical Plots
Statistical plots include the following:
.errorbar(x, y, xerr=xerr, yerr=yerr)
works similarly toscatter()
but additionally accepts error margins in either orboth the x- and y-directions.xerr
andyerr
may be either 1\(\times n\) or2\(\times n\) (for asymmetric error bars) where \(n\)is the length of x and y.Upper and lower limits kwargs,
uplims
,lolims
,xlolims
, andxuplims
accept 1D boolean arrays whereTrue
indicates that the upper, lower, left, and/or righterror bars (respectively) of the given point are limits.Note:xerr
oryerr
at a point with a limit muststill have a suitable non-zero fill value in order to draw anappropriately-sized limit arrow.errorbar()
by default connects sequential data points witha line unless you setlinestyle=''
(yes, that’s differentfrom how it’s done forplot()
).
.hist(x, bins=10)
draws 1D histograms wherebins
can beeither an integer number of bins or a fixed array of bin edges,and bins may also be log-scaled in height..hist2d(x, y, bins=100)
draws a 2D histogram wherebins
can be an integer number of bins along both axes, a 2-tuple ofiteger numbers of bins along each axis individually, a 1D array ofbin edges along both axes, or a 2\(\times\)n array of binedges, one 1D array per axis.Bins are colored by counts according to the colormap andintensity scale normalization (linear, log, other) of yourchoice.
.hexbin(x, y, C=None, gridsize=100)
is functionally somewherebetweenhist2d
andimshow
(see section on grid data);x
andy
can be scattered data or the coordinates of the dataC
..boxplot(X)
takes an array-likeX
, represening n 1Ddistributions, plots a rectangle spanning the upper and lowerquartiles with a line marking the median and errorbar-like“whiskers” extending 1.5 times the interquartile range from thebox..violinplot(X)
is similar toboxplot()
but instead of theboxes and whiskers, it shows bidirectional histogram KDEs(basically smoothed histograms) of each distribution spanning thefull range of the data..ecdf(x)
plots the empirical cumulative distribution functionofx
, which is very similar to usinghist(x, bins=len(x), cumulative=True)
, i.e. it’s a cumulativestepwise function where every point is its own step..eventplot(X)
(rare outside neurology) plots sequences of parallel lines at thepositions given byX
, which may be 1D or 2D depending onwhether there are multiple sequences of events to plot or just 1..pie(wedges)
plots a pie chart given relative or absolutewedge sizes. Avoid this. It’s inefficient.
It’s hard to load a good data set to demonstrate statistical plots without Pandas and Seaborn, and since we’ll cover those tomorrow, it’s not worth the effort to avoid them. Seaborn includes some public datasets accessible via the load_dataset()
function, which it loads into a Pandas DataFrame. The Penguins dataset is a collection of real measurements of the bills and flippers of 3 species of penguin: Adelaide, Chinstrap, and Gentoo.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport pandas as pdimport seaborn as sbpenguins = sb.load_dataset('penguins') #this loads into a Pandas DataFramechinstrap = penguins.loc[penguins['species']=='Chinstrap']#mock up some individual error bars (pretend those penguins are squirmy)xs = chinstrap['bill_length_mm']ys = chinstrap['flipper_length_mm']rng = np.random.default_rng()xerrs = abs(rng.normal(xs.mean(), xs.std(), size=len(xs))-xs.mean())yerrs = abs(rng.normal(ys.mean(), ys.std(), size=len(ys))-ys.mean())fig, ax = plt.subplots()ax.errorbar(xs,ys, xerr=xerrs,yerr=yerrs, capsize=2,linestyle='',color='b', marker='.',ecolor='k')ax.set_xlabel('Bill length [mm]')ax.set_ylabel('Flipper length [mm]')
Text(0, 0.5, 'Flipper length [mm]')
To combine the hist()
and hist2d()
examples, let’s make a plot of joint and marginal distributions, based on the official demo with histogram marginal distributions around a scatter plot. A proper corner plot is much simpler to do with Seaborn, but this will demonstrate not just of how the histogram functions look, but how to scale and position connected subplots that are not the same size as the main plot, and how to place a colorbar within a subplot mosaic.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport pandas as pdimport seaborn as sbpenguins = sb.load_dataset('penguins') #this loads into a Pandas DataFramedef corner_2p(xdata, ydata, ax2d, ax_histx, ax_histy): # no labels ax_histx.tick_params(axis="x", labelbottom=False) ax_histy.tick_params(axis="y", labelleft=False) nbins = int(np.ceil(2*len(xdata)**(1/3))) #Rice binning rule # the central 2D histogram: n,xb,yb,img = ax2d.hist2d(xdata, ydata, bins = [nbins,nbins]) #use x- & y-bins from 2D histogram to align them ax_histx.hist(xdata, bins=xb) ax_histy.hist(ydata, bins=yb, orientation='horizontal') ax_histx.sharex(ax2d) ax_histy.sharey(ax2d) return imgfig, axd = plt.subplot_mosaic("a.;Bc;d.",layout="constrained", height_ratios=[1, 3.5, 0.5], width_ratios=[3.5, 1], figsize=(6,6), dpi=100)jointhist = corner_2p(penguins.dropna()['bill_length_mm'], penguins.dropna()['flipper_length_mm'], axd['B'], axd['a'], axd['c'])axd['B'].set_xlabel('Bill length [mm]')axd['B'].set_ylabel('Flipper length [mm]')cb = fig.colorbar(jointhist,cax=axd['d'], orientation='horizontal')cb.set_label('Number of Penguins')
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport pandas as pdimport seaborn as sbpenguins = sb.load_dataset('penguins') #this loads into a Pandas DataFramespecs = penguins.dropna().groupby(['species'])spbills = {k:specs.get_group((k,))['bill_length_mm'].to_numpy() for k in penguins['species'].unique()}#Box and Violin plotsfig,axes = plt.subplots(ncols=2, sharey=True)axes[0].boxplot( list(spbills.values()) )axes[0].set_ylabel('Bill Length [mm]')axes[1].violinplot( list(spbills.values()), showmedians=True)for ax in axes: ax.set_xticks([x+1 for x in range(3)], labels=list(spbills.keys()) ) ax.set_xlabel('Penguin Species')
Plots for Gridded Data
.contour(X, Y, Z)
and.contourf(X, Y, Z)
are nearlyidentical except that the former plots only line contoursaccording to the height/intensity ofZ
on the gridX,Y
,while the latter fills between the lines.The line contour function
contour()
, if assigned to avariable, has aclabel()
method you can call to print thenumerical value of each level along each of the contours.
.imshow(Z, origin='upper')
can plot and optionally interpolate a 2D intensity image, a $n\times m \times$3 stack of RGB images, or a $n\times m \times$4 stack of RGB-A images (A is a fractional opacity value between 0 and 1), on a grid of rectangular pixels whose aspect ratio is determined by theaspect
kwarg (default'equal'
).Typically, one must set
origin='lower'
to render the image the right way up.If each pixel is an integer width in the desired units, one canuse the
extent
kwarg to assign the coordinates (less reliable than standard coordinate projections).
.pcolormesh(X, Y, Z)
is slower thanimshow
but gives morecontrol over the shape of the grid because grid pixels need nothave right-angled corners or straight sides..pcolor(X, Y, Z)
is a generalized version ofpcolormesh()
that allows one to pass masked gridsX
andY
in additionto masked imagesZ
, but because of this it is much slower..barbs([X, Y,] U, V, [C])
is a specialized plot type formeteorologists that uses a bar with spikes and flags to indicatewind speed and direction..quiver([X, Y,] U, V, [C])
plots a 2D field of arrows whosesize and length are proportional to the magnitudes of U and V.Including X and Y establishes a coordinate grid that lets onespecify U and V in units of the grid.
C lets you assign the arrows a color map according to theirmagnitude.
.streamplot([X, Y,] U, V)
draws streamlines of a vector flowwith a streamline density controlled by thedensity
kwarg.
For barbs()
, quiver()
, and streamplot()
, X,Y
arecoordinates (optional), U,V
are the mandatory x and y componentsof the vectors, and C
is the color (optional). For all of theabove where X
and Y
appear, X
and Y
must generally becomputed with np.meshgrid()
.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inline#mock up some datax = np.arange(-3.0, 3.0, 0.025)y = np.arange(-2.0, 2.0, 0.025)X, Y = np.meshgrid(x, y)Z1 = np.exp(-X**2 - Y**2)Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)Z = (Z1 - Z2) * 2fig, axes=plt.subplots(nrows=2,figsize=(5,5))CS = axes[0].contour(X,Y,Z)axes[0].clabel(CS, inline=True, fontsize=10)CF = axes[1].contourf(X,Y,Z, cmap=mpl.colormaps['magma'])fig.colorbar(CF) #yes, colorbars for contours are automatically discretized
<matplotlib.colorbar.Colorbar at 0x7fbac69cecc0>
import numpy as npimport matplotlib.pyplot as plt%matplotlib inline# 11x7 gridXs, Ys = np.meshgrid(np.arange(-0.5, 10, 1), np.arange(4.5, 11, 1))Xskew = Xs + 0.2 * Ys # tilt the coordinates.Yskew = Ys + 0.3 * Xsfig, ax = plt.subplots()ax.pcolormesh(Xskew, Yskew, np.random.rand(6, 10))
<matplotlib.collections.QuadMesh at 0x7fbac660e120>
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineX, Y = np.meshgrid(np.arange(0, 2 * np.pi, .2), np.arange(0, 2 * np.pi, .2))U = np.cos(X)V = np.sin(Y)fig, axs = plt.subplots(ncols=2, nrows=2,dpi=200,figsize=(7,7))fig.subplots_adjust(hspace=0.3)M = np.hypot(U, V)# Scale is inverse. Width is fraction of plot size; start around ~0.005#1. imshow()C2 = axs[0,0].imshow(M,cmap='plasma', extent=[np.min(X),np.max(X), np.min(Y),np.max(Y)])axs[0,0].set_title('Imshow of vector magnitudes')#2. quiver()Q = axs[0,1].quiver(X, Y, U, V, scale_units='inches',scale=12,width=0.004)qk = axs[0,1].quiverkey(Q, 0.74, 0.51, np.max(M), r'${:.1f} \frac{{m}}{{s}}$'.format(np.max(M)), labelpos='W',coordinates='figure')#labelpos can be N, S, E, or Waxs[0,1].set_title('Quiver')#3. streamplot()SP = axs[1,0].streamplot(X, Y, U, V, color=M, linewidth=1.2,cmap='cividis')axs[1,0].set_title('Streamplot')#4. barbs()barbs = axs[1,1].barbs(X[::6,::6], Y[::6,::6], 10*U[::6,::6], 10*V[::6,::6])axs[1,1].set_title('Barbs (downsampled)')plt.show()
Plots for Data on Irregular or Non-Cartesian Grids
Most of the following functions accept a Triangulation
object inlieu of x
and y
, and indeed do the triangulation internallyif x
and y
are provided. If you decide to provide your owntriangulation, it will need to be computed with the Triangulation
function of matplotlib.tri
.mpl.tri.Triangulation(x, y, triangles=None)
computes Delaunaytriangles from x
and y
vertex coordinates if triangles
isNone
, or takes an array of 3-tuples to specify the triangle sidesfrom indexes of x
and y
in anticlockwise order.
.tricontour(Triangulation, z)
or.tricontour(x, y, z)
drawcontour lines (the number of which can be specified with thelevels
kwarg) on an unstructured triangular grid according tothe intensityz
..tricontourf(Triangulation, z)
or.tricontourf(x, y, z)
are the same as the previous function except instead ofdilineating the edges of each level with a thin line, every levelis shaded across its full width..triplot(Triangulation)
or.triplot(x, y)
draw only theedges of a triangular mesh..tripcolor(Triangulation, c)
or.tripcolor(x, y, c)
shadethe triangles of a triangular mesh according to the arrayc
togenerate a pseudocolor image whose “pixels” are triangles.
The latter 2 functions are also handy for plotting functions that areregular in a sense but not with respect to a Cartesian grid; theirutility in that respect shines more in 3D.
The contouring functions might be tempting if you have scattereddata, but if what you want to contour is point density, you’re betteroff making a histogram or contouring a kernel density estimation. Thetricontour
and tricontourf
functions are only for data whereeach triangle vertex is already associated with some z-value, andwhere adjacent z-values are spatially correlated.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlineimport matplotlib.tri as tri#Mock up data of something that looks like vaguely like an epidemic or something similarnp.random.seed(19990101)rads = np.random.lognormal(size=100)angs = np.random.uniform(low=0.0, high=2*np.pi, size=100)xs = (rads * np.cos(angs))ys = (rads * np.sin(angs))zs = np.random.randint(1,high=50, size=100)fig,ax = plt.subplots()ax.tricontourf(xs,ys,zs)ax.triplot(xs,ys,'k.-', lw=0.5)plt.show()
Volumetric Plots
To render in 3D, all functions below must be plotted on figure with fig, ax = plt.subplots(subplot_kw = {"projection": "3d"})
or an axes instance with ax = plt.subplot(nrows, ncols, index, projection = "3d")
:
Many normally pairwise functions accept a 3rd parameter:
.scatter(x, y, z)
,.plot(x, y, z)
,.stem(x, y, z)
,.errorbar(x, y, z)
, etc.For scattered data, it is good to draw a lines from thepoints to some baseline, but
stem()
is not necessarily agood way to do this because of the formatting limitations andbecause there is nozorder
kwarg.
.voxels([x, y, z], filled)
(filled
is a 3D boolean mask)fills a volume with cubic pixel blocks..plot_surface(X, Y, Z)
(X
,Y
, andZ
are computedwithnp.meshgrid()
) essentially makes an elevation map wherethe surface is shaded like it would be for an image plotted withimshow
orhist2d
..plot_wireframe(X, Y, Z)
(X
,Y
, andZ
are computedwithnp.meshgrid()
) plots the surface so it resembles a net orcurved grid..plot_trisurf(x, y, z)
is similar toplot_wireframe
exceptthe net is made of triangles..bar3d(x, y, bottom, width, depth, top, shade=True)
can eitherplot multiple rows of 2D bar plots stacked depthwise, or make afigure that looks like a Manhattan skyline..quiver(x, y, z, u, v, w)
plots a 3D field of arrows where(x,y,z) define the arrow positions and (u,v,w) defines theirdirections.Not recommended in 3D, and especially not with variable color:the arrow tips tend to be truncated and different pieces ofeach arrow may get a different color.
Below is a sample of how scatter(x,y,z)
handles depth, and howyou can achieve something similar with stem()
if you want yourreaders to be able to read off coordinates to some extent. The plotsare of the positions of the Sun and its nearest 20 stellar neighbors.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinex,y,z,c = np.genfromtxt('docs/day1/solar_neighborhood.txt', encoding='ascii', dtype=[('x','<f8'),('y','<f8'),('z','<f8'), ('c','<U12')], converters={3:lambda s: 'tab:'+str(s)}, unpack=True)zsun = abs(min(z))z = z+zsunfig, axes = plt.subplots(ncols=2, subplot_kw = {"projection": "3d"}, dpi=150)#Left: scatter3daxes[0].scatter(x,y,z,c=list(c))#Right: stem3dfor clr in set(c): idx = np.where(c==clr) if 'orange' in clr: clr='m' elif 'olive' in clr: clr='y' else: clr=clr[4] axes[1].stem(x[idx],y[idx],z[idx], linefmt=str(clr+':'), markerfmt=str(clr+'o'),bottom=0.0, basefmt=" ")for ax in axes: ax.stem([0],[0],[zsun], linefmt='k--',markerfmt='k*', bottom=0.0, basefmt=" ", label='Sun') ax.legend()plt.title('Nearest 20 Stars (Scale in LY)')plt.show()
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinefrom matplotlib import cmfig, axes = plt.subplots(ncols=2, subplot_kw={"projection":"3d"}, dpi=180, figsize=(5,11))fig.subplots_adjust(wspace=0.8)# Make data.X = np.arange(-5, 5, 0.25)Y = np.arange(-5, 5, 0.25)X, Y = np.meshgrid(X, Y)R = np.sqrt(X**2 + Y**2)Z = np.cos(R)# Plot the surfaces.surf = axes[0].plot_surface(X, Y, Z, cmap=cm.RdYlBu, linewidth=1, antialiased=True)axes[0].set_xlabel('x')axes[0].set_ylabel('y')axes[0].set_zlabel('z')mesh = axes[1].plot_wireframe(X, Y, Z, color='k', linewidth = 0.5, rstride=3, cstride=3)axes[1].contourf(X, Y, Z, zdir='z', offset=-1, cmap='coolwarm')axes[1].contourf(X, Y, Z, zdir='x', offset=-5, cmap='coolwarm')plt.show()
Formatting and Placing Plot Elements
Placing Legends and Text
Text. There are 2 functions for adding text to plots at arbitrarypoints: .annotate()
and .text()
.text()
is base function; it only adds and formats text (e.g.ha
andva
set horizontal and vertical alignment).annotate()
adds kwargs to format connectors between pointsand text; coordinates for point and text are specified separately
Positions for both are given in data coordinates unless oneincludes transform=ax.transAxes
. ax.transAxes
switches fromdata coordinates to axes-relative coordinates where (0,0) is lowerleft corner of the axes object, (1,1) is the top right corner of theaxes, and values $<$0 or $>$1 are outside of the axes (figure areawill stretch to accommodate up to a point).
Legends. Typically, it’s enough to just use plt.legend()
orax.legend()
if you want to label multiple functions on the sameplot.
Legends can be placed with the
loc
kwarg according to a numberfrom 0 to 10, or with a descriptive string like'upper left'
or'lower center'
. In the number code system, 0 (default)tells matplotlib to just try to minimize overlap with data, andthe remaining digits represent ninths of the axes area (“centerright” is duplicated for some reason).You can also arrange the legend entries in multiple columns bysetting the
ncols
kwarg to an integer >1, which can help ifspace is more limited vertically than horizontally.Legend placement via
bbox_to_anchor
uses unit-axes coordinates(i.e. the same coordinates described above astransform=ax.transAxes
) by default, and can specify anycoordinates on or off the plot area (x and y are within the plotarea if they are between 0 and 1, and outside otherwise).Whole-figure legends (i.e.
fig.legend()
) can use a 3-wordstring where the first word is “outside”, likeloc='outside center right'
.
Mathtext
Most journals expect that you typeset all variables and math scriptsso they appear the same in your plots main text. Matplotlib nowsupports most LaTeX mathcommands,but you need to know some basic LaTeX syntax, some of which iscovered in that link. For more information, you can refer to theWikiBooks documentation on LaTeXmath, startingwith the Symbols section.
LaTeX may need to be installed separately for Matplotlib versionsearlier than 3.7, or for exceptionally obscure symbols orodd-sized delimiters.
Unfortunately, Python and LaTeX both use curly braces ({}
) aspart of different functions, so some awkward adjustments had to bemade to resolve the collision.
In
str.format()
, all curly braces ({}
) associated withLaTeX commands must be doubled ({{}}
), including nestedbraces. An odd-numbered set of nested curly brace pairs will beinterpreted as a site for string insertion.Many characters also require the whole string to have an
r
(for raw input) in front of the first single- or double-quote,like \(\times\) (rendered as'$\times$'
), \(\pm\) or\(\mp\)(rendered as'$\pm$'
and'$\mp$'
respectively), or most Greek letters.Most basic operator symbols (+, -, /, >, <, !, :, |, [], ()) canbe used as-is, but some that have functional meanings in LaTeX,Python, or both (e.g. $ and %) must be preceded by a single-(LaTeX command symbols only) or double-backslash (\\) to escapetheir typical usage.
Spaces within any character sequence between two
$
s are notrendered; they only exist to separate alphabetic characters fromcommands. You can insert a space with\;
if you don’t want tosplit up the LaTeX sequence to add spaces.
You can use string insertion inside of formatting operators like the super- and subscript commands, but it can require a lot of sequential curly braces. The following is an example demonstrating some tricky typesetting. Note that you generally cannot split the string text over multiple lines because the backslash has other essential uses to the typesetting.
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinev_init=15.1error_arr=[-0.4,0.3]fig,ax=plt.subplots(dpi=120,figsize=(5,5))ax.set_aspect('equal') #arrowheads will slant if axes are not equalax.arrow(0,0,10.68,10.68,length_includes_head=True,color='b', head_width=0.4)ax.text(6, 5.4, r"$|\vec{{v}}_{{\mathrm{{init}}}}|$ = ${:.1f}_{{{:.1}}}^{{+{:.1}}}\;\mathrm{{m\cdot s}}^{{-1}}$".format(v_init,*error_arr), ha='center',va='center',rotation=45.,size=14, color='b')ax.set_xlim(0,12)ax.set_ylim(0,12)plt.show()
Formatting Axes
Axes objects (the ax
in fig,ax=plt.subplots()
) have dozens ofmethods and attributes apart from the function methods covered in theStandard Available Plot Types section. Most of the methods that areplotting functions are for formatting and labeling the axes. Amongthe most commonly used, some of which you’ve already seen, are:
ax.set_xlabel(str)
andax.set_ylabel(str)
, which addtitles to the axes, as was already shown.ax.set_title(str)
adds a title to the top of the plotax.legend()
adds a box with the names and markers of eachfunction or data set on a plotax.grid()
adds grid lines at the locations of major axes ticksax.set_xlim()
andax.set_ylim()
, which change the lowerand upper bounds of the axes and readjust the shape of the dataand axes scale increments accordinglyax.set_xscale()
andax.set_yscale()
let you change thespacing of the increments on each axes from linear to log, logit,symlog (log scaling that allows for numbers $\leq$0), asinh,mercator, function*, or functionlog*.*
'function'
requires one to define both forward andreverse functions for transforming to/from linear and pass themas tuple of function names (e.g. as inax.set_yscale('function', functions=(forward, inverse))
).'functionlog'
is similar but additionally renders the axeswith log-scaling.
ax.invert_xaxis()
andax.invert_yaxis()
do exactly whatthey sayax.secondary_xaxis()
andax.secondary_yaxis()
addsecondary axes on the top and right sides, respectively, which maybe tied to the primary axes by transformations or may be totallyunconnectedThese are NOT necessary to mirror the x and y axis ticks to thetop and right; for that, you can just set
ax.tick_params(axis='both', which='both', top=True, right=True)
wherewhich
specifies the set of ticks to modify (“major”,“minor”, or “both”).
ax.get_xticks()
andax.get_yticks()
return arrays of thecurrent positions of the ticks along their respective axes, indata coordinates. Handy for use in computing the transformationsfor secondary axes or reformatting tick labels.
Any axes methods that have set
in the name have a get
counterpart that returns the current value(s) of whatever the set
method would set or overwrite.
Cautionary notes. Scales that are neither linear nor logarithmicare not suitable for histograms, contours, or image-like data.Contours don’t tend to work well with log axes either: you’ll need towork in log units and use tick label formatters to override thelabels (next section).
Axis Ticks and Locators
Usually automatic tick spacing is fine. However, you may need tomodify the auto-generated tick labels and locators, or set thementirely by hand, if you want to have:
Units with special formats or symbols (e.g. dates and/or times,currencies, coordinates, etc.)
Irrational units (e.g. multiples of \(e\), fractions of\(\pi\), etc.)
Qualitative variables (e.g. countries, species, relative sizecategories, etc.)
Axis tick labels centered between major ticks
Secondary axes that are transformations of the primary axes
Custom or power-law axis scales
Log-, symlog-, or asinh scaling with labels on every decade andvisible minor ticks over >7 decades
on one of more of your axes, or if you want any of the above on acolorbar. In these situations, you’ll need to manually adjust theticks using various Locator functions kept in matplotlib.ticker
as arguments of ax.<x|y>axis.set_<major|minor>_locator()
methods(the getter counterparts of these functions will probably come inhandy here). Matplotlib also has ample support, templates, andexplicit demos formost those situations, but there are a few situations wheredocumentation is poor.
Let’s walk through the following example demonstrating bothLogLocator()
(in which documentation on the numticks
andsubs
kwargs are not very good) andax.secondary_xaxis('top', functions=(prim2sec,sec2prim))
:
import numpy as npimport matplotlib.pyplot as plt%matplotlib inline#blackbody curve for the temperature of the sun# as a function of wavelengthc = 2.998*10**8.k_b = 1.380649*10**-23.hc = (2.998*10**8.)*(6.626*10**-34.)def bb(wvl,T): return ((2*hc*c)/(wvl**5)) * 1/(np.exp(hc/(wvl*k_b*T)) - 1)wvs = np.logspace(-7.2,-3.0,471) #x-valuesbb5777 = bb(wvs,5777.) #y-values#===============================================================import matplotlib.ticker as ticksfig, ax = plt.subplots(dpi=120, figsize=(4,4))ax.plot(wvs*10**9,bb5777,'k-')# 1 nm = 10^-9 m, 1 THz = 10^12 Hzsecax = ax.secondary_xaxis('top',functions=(lambda x: 1000*c/x, lambda x: 0.001*c/x))#1st func. is primary-to-secondary#2nd func. is secondary-to-primaryax.set_xscale('log')ax.set_yscale('log')# PAY SPECIAL ATTENTION TO THE NEXT 4 LINESax.yaxis.set_major_locator(ticks.LogLocator(base=10,numticks=99))ax.yaxis.set_minor_locator(ticks.LogLocator(base=10.0,subs=(0.2,0.4,0.6,0.8), numticks=99))ax.yaxis.set_minor_formatter(ticks.NullFormatter())ax.tick_params(axis='y',which='both',right=True)ax.set_xlabel('Wavelength [nm]')secax.set_xlabel('Frequency [THz]')ax.set_ylabel('Intensity [W(m$\cdot$sr$\cdot$nm)$^{-1}$]')plt.show()
Log scaling is very common, so it’s worth going over these gotchas ofthe ticker.LogLocator()
function before they make you waste halfa day:
numticks
must be at least as large as the total number ofmajor or minor axis ticks needed to span the axis, or else thewhole line will be ignored and you’ll get a blank axis. Eithercalculate it in advance or just use a number large enough toborder on silly (like 99).For minor ticks, include the
subs
kwarg and list relativeincrements between but not including the major ticks where youwant minor ticks to be marked. Note thatsubs
only spans thedistance from one major axis tick to the next, whilenumticks
must be enough to span the entire axis.If you show minor ticks, add
ax.<x|y>axis.set_minor_formatter(ticks.NullFormatter())
toturn off minor tick labels, otherwise your axis tick labels will be very crowded.
Placing and Formatting Color Bars
Colorbars are methods of Figure
, not Axes
, in the explicitAPI. Each axis object must be passed to each colorbar()
commandexplicitly, and the first arg must be a mappable: the plot itself,not the axis object.
If there are multiple subplots, colorbar()
takes an ax
kwargto specify which to attach it to, which can be different from theaxes that the colors refer to (this can be used to allow the samecolorbar to reflect multiple plots with the same coloration).
The extend
kwarg lets you indicate that 1 or both ends of thecolorbar have been truncated to maintain contrast. There is also ashrink
kwarg that helps one resize the colorbar to match a plot’swidth or height (depending on orientation), because Matplotlib oftenmakes the colorbar too large by default.
Ticks and locators for color bars are inferred from the plot bydefault, but can be overriden using the ticks
and format
kwargs of colorbar()
.
The
ticks
kwarg accepts all the same locator functions asax.[x|y]axis.set_[major|minor]_locator()
The
format
kwarg accepts the same codes for formatting numbersas the curly braces dostr.format()
statements, or a customformatter function passed toticker.FuncFormatter()
. Thismeans you can useformat
to force alternative displays ofscientific notation, percentages*, etc. (* the normal percentageformatting command doesn’t seem to work for some versions, soyou’ll need to use theFuncFormatter
approach).
import numpy as npimport matplotlib.pyplot as plt%matplotlib inlinefig, (ax1, ax2) = plt.subplots(nrows=2, figsize=[3,6], dpi=120)plt.subplots_adjust(hspace=-0.1)img1 = ax1.imshow(Z1, cmap='magma')img2 = ax2.imshow(Z2, norm='log', vmin=0.01)cbar1 = fig.colorbar(img1, ax=ax1, extend='min',orientation='horizontal', format= ticks.FuncFormatter(lambda x, _: f"{x:.0%}"))# The _ is because FuncFormatter passes in both the label and the position,# but we don't need the latter. The _ lets us dump the position.cbar1.set_label('Fractional intensity')cbar2 = fig.colorbar(img2, ax=ax2, shrink=0.5, extend='both', format="{x:.0E}")plt.show()
Key Points
Matplotlib is the essential Python data visualization package,with nearly 40 different plot types to choose from depending onthe shape of your data and which qualities you want to highlight.
Almost every plot will start by instantiating the figure,
fig
(the blank canvas), and 1 or more axes objects,ax
, withfig, ax = plt.subplots(*args, **kwargs)
.Most of the plotting and formatting commands you will use aremethods of
Axes
objects, but a few, likecolorbar
aremethods of theFigure
, and some commands are methods both.