In [1]:
%matplotlib notebook
import numpy as np
import matplotlib.pyplot as plt
In the previous parts, you learned how matplotlib organizes plot-making by figures and axes. We broke down the components of a basic figure and learned how to create them. You also learned how to add one or more axes to a figure, and how to tie them together. You even learned how to change some of the basic appearances of the axes. Finally, we went over some of the many plotting methods that matplotlib has to draw on those axes. With all that knowledge, you should be off making great and wonderful figures.
Of course! While the previous sections may have taught you some of the structure and syntax of matplotlib, it did not describe much of the substance and vocabulary of the library. This section will go over many of the properties that are used throughout the library. Note that while many of the examples in this section may show one way of setting a particular property, that property may be applicible elsewhere in completely different context. This is the "language" of matplotlib.
This is, perhaps, the most important piece of vocabulary in matplotlib. Given that matplotlib is a plotting library, colors are associated with everything that is plotted in your figures. Matplotlib supports a very robust language for specifying colors that should be familiar to a wide variety of users.
First, colors can be given as strings. For very basic colors, you can even get away with just a single letter:
Other colornames that are allowed are the HTML/CSS colornames such as "burlywood" and "chartreuse" are valid. See the full list of the 147 colornames. They allow "grey" where-ever "gray" appears in that list of colornames. All of these colornames are case-insensitive.
Colors can also be specified by supplying an HTML/CSS hex string, such as '#0000FF'
for blue.
A gray level can be given instead of a color by passing a string representation of a number between 0 and 1, inclusive. '0.0'
is black, while '1.0'
is white. '0.75'
would be a lighter shade of gray.
You may come upon instances where the previous ways of specifying colors do not work. This can sometimes happen in some of the deeper, stranger levels of the code. When all else fails, the universal language of colors for matplotlib is the RGB[A] tuple. This is the "Red", "Green", "Blue", and sometimes "Alpha" tuple of floats in the range of [0, 1]. One means full saturation of that channel, so a red RGBA tuple would be (1.0, 0.0, 0.0, 1.0)
, whereas a partly transparent green RGBA tuple would be (0.0, 1.0, 0.0, 0.75)
. The documentation will usually specify whether it accepts RGB or RGBA tuples. Sometimes, a list of tuples would be required for multiple colors, and you can even supply a Nx3 or Nx4 numpy array in such cases.
In functions such as plot()
and scatter()
, while it may appear that they can take a color specification, what they really need is a "format specification", which includes color as part of the format. Unfortunately, such specifications are string only and so RGB[A] tuples are not supported for such arguments (but you can still pass an RGB[A] tuple for a "color" argument).
Note, oftentimes there is a separate argument for "alpha" where-ever you can specify a color. The value for "alpha" will usually take precedence over the alpha value in the RGBA tuple. There is no easy way around this problem.
In [ ]:
# %load exercises/3.1-colors.py
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, , t, t**2, , t, t**3, )
plt.show()
Markers are commonly used in plot()
and scatter()
plots, but also show up elsewhere. There is a wide set of markers available, and custom markers can even be specified.
marker | description | marker | description | marker | description | marker | description | |||
---|---|---|---|---|---|---|---|---|---|---|
"." | point | "+" | plus | "," | pixel | "x" | cross | |||
"o" | circle | "D" | diamond | "d" | thin_diamond | |||||
"8" | octagon | "s" | square | "p" | pentagon | "*" | star | |||
"|" | vertical line | "_" | horizontal line | "h" | hexagon1 | "H" | hexagon2 | |||
0 | tickleft | 4 | caretleft | "<" | triangle_left | "3" | tri_left | |||
1 | tickright | 5 | caretright | ">" | triangle_right | "4" | tri_right | |||
2 | tickup | 6 | caretup | "^" | triangle_up | "2" | tri_up | |||
3 | tickdown | 7 | caretdown | "v" | triangle_down | "1" | tri_down | |||
"None" | nothing | None |
nothing | " " | nothing | "" | nothing |
In [3]:
xs, ys = np.mgrid[:4, 9:0:-1]
markers = [".", "+", ",", "x", "o", "D", "d", "", "8", "s", "p", "*", "|", "_", "h", "H", 0, 4, "<", "3",
1, 5, ">", "4", 2, 6, "^", "2", 3, 7, "v", "1", "None", None, " ", ""]
descripts = ["point", "plus", "pixel", "cross", "circle", "diamond", "thin diamond", "",
"octagon", "square", "pentagon", "star", "vertical bar", "horizontal bar", "hexagon 1", "hexagon 2",
"tick left", "caret left", "triangle left", "tri left", "tick right", "caret right", "triangle right", "tri right",
"tick up", "caret up", "triangle up", "tri up", "tick down", "caret down", "triangle down", "tri down",
"Nothing", "Nothing", "Nothing", "Nothing"]
fig, ax = plt.subplots(1, 1, figsize=(14, 4))
for x, y, m, d in zip(xs.T.flat, ys.T.flat, markers, descripts):
ax.scatter(x, y, marker=m, s=100)
ax.text(x + 0.1, y - 0.1, d, size=14)
ax.set_axis_off()
plt.show()
In [ ]:
# %load exercises/3.2-markers.py
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, , t, t**2, , t, t**3, )
plt.show()
Line styles are about as commonly used as colors. There are a few predefined linestyles available to use. Note that there are some advanced techniques to specify some custom line styles. Here is an example of a custom dash pattern.
linestyle | description |
---|---|
'-' | solid |
'--' | dashed |
'-.' | dashdot |
':' | dotted |
'None' | draw nothing |
' ' | draw nothing |
'' | draw nothing |
Also, don't mix up ".-" (line with dot markers) and "-." (dash-dot line) when using the plot
function!
In [5]:
fig = plt.figure()
t = np.arange(0.0, 5.0, 0.2)
plt.plot(t, t, '-', t, t**2, '--', t, t**3, '-.', t, -t, ':')
plt.show()
It is a bit confusing, but the line styles mentioned above are only valid for lines. Whenever you are dealing with the linestyles of the edges of "Patch" objects, you will need to use words instead of the symbols. So "solid" instead of "-", and "dashdot" instead of "-.". This issue is be fixed for the v2.1 release and allow these specifications to be used interchangably.
In [6]:
fig, ax = plt.subplots(1, 1)
ax.bar([1, 2, 3, 4], [10, 20, 15, 13], ls='dashed', ec='r', lw=5)
plt.show()
With just about any plot you can make, there are many attributes that can be modified to make the lines and markers suit your needs. Note that for many plotting functions, matplotlib will cycle the colors for each dataset you plot. However, you are free to explicitly state which colors you want used for which plots. For the plt.plot()
and plt.scatter()
functions, you can mix the specification for the colors, linestyles, and markers in a single string.
In [7]:
fig = plt.figure()
t = np.arange(0., 5., 0.2)
# red dashes, blue squares and green triangles
plt.plot(t, t, 'r--', t, t**2, 'bs', t, t**3, 'g^')
plt.show()
Property | Value Type |
---|---|
alpha | float |
color or c | any matplotlib color |
dash_capstyle | ['butt', 'round' 'projecting'] |
dash_joinstyle | ['miter' 'round' 'bevel'] |
dashes | sequence of on/off ink in points |
drawstyle | [ ‘default’ ‘steps’ ‘steps-pre’ |
‘steps-mid’ ‘steps-post’ ] | |
linestyle or ls | [ '-' '--' '-.' ':' 'None' ' ' ''] |
and any drawstyle in combination with a | |
linestyle, e.g. 'steps--'. | |
linewidth or lw | float value in points |
marker | [ 0 1 2 3 4 5 6 7 'o' 'd' 'D' 'h' 'H' |
'' 'None' ' ' None '8' 'p' ',' |
|
'+' 'x' '.' 's' '*' '_' '|' | |
'1' '2' '3' '4' 'v' '<' '>' '^' ] | |
markeredgecolor or mec | any matplotlib color |
markeredgewidth or mew | float value in points |
markerfacecolor or mfc | any matplotlib color |
markersize or ms | float |
solid_capstyle | ['butt' 'round' 'projecting'] |
solid_joinstyle | ['miter' 'round' 'bevel'] |
visible | [True False ] |
zorder | any number |
In [ ]:
# %load exercises/3.3-properties.py
t = np.arange(0.0, 5.0, 0.1)
a = np.exp(-t) * np.cos(2*np.pi*t)
plt.plot(t, a, )
plt.show()
Another very important property of many figures is the colormap. The job of a colormap is to relate a scalar value to a color. In addition to the regular portion of the colormap, an "over", "under" and "bad" color can be optionally defined as well. NaNs will trigger the "bad" part of the colormap.
As we all know, we create figures in order to convey information visually to our readers. There is much care and consideration that have gone into the design of these colormaps. Your choice in which colormap to use depends on what you are displaying. In mpl, the "jet" colormap has historically been used by default, but it will often not be the colormap you would want to use.
In [ ]:
# %load http://matplotlib.org/mpl_examples/color/colormaps_reference.py # For those with v1.2 or higher
"""
Reference for colormaps included with Matplotlib.
This reference example shows all colormaps included with Matplotlib. Note that
any colormap listed here can be reversed by appending "_r" (e.g., "pink_r").
These colormaps are divided into the following categories:
Sequential:
These colormaps are approximately monochromatic colormaps varying smoothly
between two color tones---usually from low saturation (e.g. white) to high
saturation (e.g. a bright blue). Sequential colormaps are ideal for
representing most scientific data since they show a clear progression from
low-to-high values.
Diverging:
These colormaps have a median value (usually light in color) and vary
smoothly to two different color tones at high and low values. Diverging
colormaps are ideal when your data has a median value that is significant
(e.g. 0, such that positive and negative values are represented by
different colors of the colormap).
Qualitative:
These colormaps vary rapidly in color. Qualitative colormaps are useful for
choosing a set of discrete colors. For example::
color_list = plt.cm.Set3(np.linspace(0, 1, 12))
gives a list of RGB colors that are good for plotting a series of lines on
a dark background.
Miscellaneous:
Colormaps that don't fit into the categories above.
"""
import numpy as np
import matplotlib.pyplot as plt
# Have colormaps separated into categories:
# http://matplotlib.org/examples/color/colormaps_reference.html
cmaps = [('Perceptually Uniform Sequential',
['viridis', 'inferno', 'plasma', 'magma']),
('Sequential', ['Blues', 'BuGn', 'BuPu',
'GnBu', 'Greens', 'Greys', 'Oranges', 'OrRd',
'PuBu', 'PuBuGn', 'PuRd', 'Purples', 'RdPu',
'Reds', 'YlGn', 'YlGnBu', 'YlOrBr', 'YlOrRd']),
('Sequential (2)', ['afmhot', 'autumn', 'bone', 'cool',
'copper', 'gist_heat', 'gray', 'hot',
'pink', 'spring', 'summer', 'winter']),
('Diverging', ['BrBG', 'bwr', 'coolwarm', 'PiYG', 'PRGn', 'PuOr',
'RdBu', 'RdGy', 'RdYlBu', 'RdYlGn', 'Spectral',
'seismic']),
('Qualitative', ['Accent', 'Dark2', 'Paired', 'Pastel1',
'Pastel2', 'Set1', 'Set2', 'Set3']),
('Miscellaneous', ['gist_earth', 'terrain', 'ocean', 'gist_stern',
'brg', 'CMRmap', 'cubehelix',
'gnuplot', 'gnuplot2', 'gist_ncar',
'nipy_spectral', 'jet', 'rainbow',
'gist_rainbow', 'hsv', 'flag', 'prism'])]
nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps)
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))
def plot_color_gradients(cmap_category, cmap_list):
fig, axes = plt.subplots(nrows=nrows)
fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99)
axes[0].set_title(cmap_category + ' colormaps', fontsize=14)
for ax, name in zip(axes, cmap_list):
ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
pos = list(ax.get_position().bounds)
x_text = pos[0] - 0.01
y_text = pos[1] + pos[3]/2.
fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)
# Turn off *all* ticks & spines, not just the ones with colormaps.
for ax in axes:
ax.set_axis_off()
for cmap_category, cmap_list in cmaps:
plot_color_gradients(cmap_category, cmap_list)
plt.show()
When colormaps are created in mpl, they get "registered" with a name. This allows one to specify a colormap to use by name.
In [10]:
fig, (ax1, ax2) = plt.subplots(1, 2)
z = np.random.random((10, 10))
ax1.imshow(z, interpolation='none', cmap='gray')
ax2.imshow(z, interpolation='none', cmap='coolwarm')
plt.show()
Oftentimes, you just simply need that superscript or some other math text in your labels. Matplotlib provides a very easy way to do this for those familiar with LaTeX. Any text that is surrounded by dollar signs will be treated as "mathtext". Do note that because backslashes are prevelent in LaTeX, it is often a good idea to prepend an r
to your string literal so that Python will not treat the backslashes as escape characters.
In [11]:
fig = plt.figure()
plt.scatter([1, 2, 3, 4], [4, 3, 2, 1])
plt.title(r'$\sigma_{i=15}$', fontsize=20)
plt.show()
There are two ways one can place arbitrary text anywhere they want on a plot. The first is a simple text()
. Then there is the fancier annotate()
function that can help "point out" what you want to annotate.
In [12]:
fig = plt.figure()
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(4, 1.5),
arrowprops=dict(facecolor='black', shrink=0.0))
plt.ylim(-2, 2)
plt.show()
There are all sorts of boxes for your text, and arrows you can use, and there are many different ways to connect the text to the point that you want to annotate. For a complete tutorial on this topic, go to the Annotation Guide. In the meantime, here is a table of the kinds of arrows that can be drawn
In [13]:
import matplotlib.patches as mpatches
styles = mpatches.ArrowStyle.get_styles()
ncol = 2
nrow = (len(styles)+1) // ncol
figheight = (nrow+0.5)
fig = plt.figure(figsize=(4.0*ncol/0.85, figheight/0.85))
fontsize = 0.4 * 70
ax = fig.add_axes([0, 0, 1, 1])
ax.set_xlim(0, 4*ncol)
ax.set_ylim(0, figheight)
def to_texstring(s):
s = s.replace("<", r"$<$")
s = s.replace(">", r"$>$")
s = s.replace("|", r"$|$")
return s
for i, (stylename, styleclass) in enumerate(sorted(styles.items())):
x = 3.2 + (i//nrow)*4
y = (figheight - 0.7 - i%nrow)
p = mpatches.Circle((x, y), 0.2, fc="w")
ax.add_patch(p)
ax.annotate(to_texstring(stylename), (x, y),
(x-1.2, y),
ha="right", va="center",
size=fontsize,
arrowprops=dict(arrowstyle=stylename,
patchB=p,
shrinkA=50,
shrinkB=5,
fc="w", ec="k",
connectionstyle="arc3,rad=-0.25",
),
bbox=dict(boxstyle="square", fc="w"))
ax.set_axis_off()
plt.show()
In [ ]:
# %load exercises/3.4-arrows.py
t = np.arange(0.0, 5.0, 0.01)
s = np.cos(2*np.pi*t)
plt.plot(t, s, lw=2)
plt.annotate('local max', xy=(2, 1), xytext=(3, 1.5),
arrowprops=dict())
plt.ylim(-2, 2)
plt.show()
A Patch object can have a hatching defined for it.
Letters can be combined, in which case all the specified hatchings are done. If same letter repeats, it increases the density of hatching of that pattern.
In [15]:
fig = plt.figure()
bars = plt.bar([1, 2, 3, 4], [10, 12, 15, 17])
plt.setp(bars[0], hatch='x', facecolor='w')
plt.setp(bars[1], hatch='xx-', facecolor='orange')
plt.setp(bars[2], hatch='+O.', facecolor='c')
plt.setp(bars[3], hatch='*', facecolor='y')
plt.show()
To learn more, please see this guide on customizing matplotlib.