Advanced Matplotlib Concepts
In this section we cover some more advanced topics which you won't usually use as often.
Logarithmic Scale
It is possible to set a logarithmic scale for one or both axes. This functionality is in fact only one application of a more general transformation system in Matplotlib. Each of the axes' scales are set separately using set_xscale
and set_yscale
methods which accept one parameter (with the value "log" in this case).
fig, axes = plt.subplots(1, 2, figsize=(10,4)) # nrows=1, ncols=2
axes[0].plot(x, x**2, x, np.exp(x)) # two plots y=x^2, y=e^x
axes[0].set_title("Normal scale")
axes[1].plot(x, x**2, x, np.exp(x))
axes[1].set_yscale("log")
axes[1].set_title("Logarithmic scale (y)");
Axes
Axis Ticks and Tick Labels
We can explicitly determine where we want the axis ticks with set_xticks
and set_yticks
, which both take a list of values for where on the axis the ticks are to be placed. We can also use the set_xticklabels
and set_yticklabels
methods to provide a list of custom text labels for each tick location.
fig, ax = plt.subplots(figsize=(10, 4))
ax.plot(x, x**2, x, x**3, lw=2)
ax.set_xticks([1, 2, 3, 4, 5])
# use LaTeX formatted labels
ax.set_xticklabels([r'$\alpha$', r'$\beta$', r'$\gamma$',
r'$\delta$', r'$\epsilon$'], fontsize=18)
yticks = [0, 50, 100, 150]
ax.set_yticks(yticks)
# can use python string formatting and list comprehensions
aax.set_yticklabels(['${0:5.1f}$'.format(y) for y in yticks], fontsize=18);
There are a number of more advanced methods for controlling major and minor tick placement in matplotlib figures, such as automatic placement according to different policies. See
http://matplotlib.org/api/ticker_api.html
for details.
Scientific Notation
With large numbers on axes, it is often better use scientific notation.
fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, np.exp(x))
ax.set_title("scientific notation")
ax.set_yticks([0, 50, 100, 150])
from matplotlib import ticker
formatter = ticker.ScalarFormatter(useMathText=True)
formatter.set_scientific(True)
formatter.set_powerlimits((-1,1))
ax.yaxis.set_major_formatter(formatter)
Axis Number and Label Spacing
# distance between x and y axis and the numbers on the axes
matplotlib.rcParams['xtick.major.pad'] = 5
matplotlib.rcParams['ytick.major.pad'] = 5
fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, np.exp(x))
ax.set_yticks([0, 50, 100, 150])
ax.set_title("label and axis spacing")
# padding between axis label and axis numbers
ax.xaxis.labelpad = 5
ax.yaxis.labelpad = 5
ax.set_xlabel("x")
ax.set_ylabel("y");
# restore defaults for axis number padding
matplotlib.rcParams['xtick.major.pad'] = 3
matplotlib.rcParams['ytick.major.pad'] = 3
Axis Position Adjustment
Unfortunately, when saving figures the labels are sometimes clipped, and it can be necessary to adjust the positions of axes a little bit. This can be done using subplots_adjust
.
fig, ax = plt.subplots(1, 1)
ax.plot(x, x**2, x, np.exp(x))
ax.set_yticks([0, 50, 100, 150])
ax.set_title("title")
ax.set_xlabel("x")
ax.set_ylabel("y")
fig.subplots_adjust(left=0.15, right=.9, bottom=0.1, top=0.9);
Axis Grid
With the grid
method in the axis object, we can turn on and off grid lines. We can also customize the appearance of the grid lines using the same keyword arguments as the plot
function.
fig, axes = plt.subplots(1,2,figsize=(10,3))
# default grid appearance
axes[0].plot(x,x**2, x, x**3, lw=2)
axes[0].grid(True)
# customize grid appearance
axes[1].plot(x, x**2, x, x**3, lw=2)
axes[1].grid(color='b', alpha=0.25, linestyle='dashed', linewidth=0.5)
Axis Spines
We can also change the properties of axis spines.
fig, ax = plt.subplots(figsize=(6,2))
ax.spines['bottom'].set_color('blue')
ax.spines['top'].set_color('blue')
ax.spines['left'].set_color('red')
ax.spines['left'].set_linewidth(2)
# turn off axis spine to the right
ax.spines['right'].set_color("none")
ax.yaxis.tick_left() # only ticks on the left side
Twin Axes
Sometimes it is useful to have dual x or y axes in a figure; for example, when plotting curves with different units together. Matplotlib supports this with the twinx
and twiny
functions.
fig, ax1 = plt.subplots()
ax1.plot(x, x**2, lw=2, color="blue")
ax1.set_ylabel(r"area $(m^2)$", fontsize=18, color="blue")
for label in ax1.get_yticklabels():
label.set_color("blue")
ax2 = ax1.twinx()
ax2.plot(x, x**3, lw=2, color="red")
ax2.set_ylabel(r"volume $(m^3)$", fontsize=18, color="red")
for label in ax2.get_yticklabels():
label.set_color("red")
Coordinate Axis (no frame)
As seen from the examples above the default is a coordinate axis framing box. If we want a set of axes centred at \((0,0)\) we can use the spines()
method, and the set_position
submethod.
fig, ax = plt.subplots()
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0)) # set position of x spine to y=0
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0)) # set position of y spine to x=0
xx = np.linspace(-0.75, 1., 100)
ax.plot(xx, xx**3);
Other 2D Plot Styles
In addition to the regular plot method, there are a number of other functions for generating different kind of plots. See the matplotlib plot gallery for a complete list of available plot types:
http://matplotlib.org/gallery.html
Some of the more useful ones are show below.
fig, axes = plt.subplots(1, 4, figsize=(12,3))
axes[0].scatter(xx, xx + 0.25*np.random.randn(len(xx))) # xx defined above
axes[0].set_title("scatter")
axes[1].step(n, n**2, lw=2)
axes[1].set_title("step")
axes[2].bar(n, n**2, align="center", width=0.5, alpha=0.5)
axes[2].set_title("bar")
axes[3].fill_between(x, x**2, x**3, color="green", alpha=0.5);
axes[3].set_title("fill_between");
Text Annotation
Annotating text in matplotlib figures can be done using the text
function. It supports LaTeX formatting just like axis label texts and titles.
fig, ax = plt.subplots()
ax.plot(xx, xx**2, xx, xx**3)
# center coordinate axes
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.spines['bottom'].set_position(('data',0)) # set position of x spine to y=0
ax.spines['left'].set_position(('data',0)) # set position of y spine to x=0
ax.text(0.1, 0.2, r"$y=x^2$", fontsize=20, color="tab:blue")
ax.text(0.65, 0.1, r"$y=x^3$", fontsize=20, color="tab:orange");
Figures with Multiple Subplots
Axes can be added to a matplotlib Figure canvas manually using fig.add_axes
or using a sub-figure layout manager such as subplots
, subplot2grid
, or gridspec
.
Subplots
subplot2grid
fig = plt.figure()
ax1 = plt.subplot2grid((3,3), (0,0), colspan=3)
ax1.text(0.5,0.5,'subplot 1',ha='center',va='center',size=14,alpha=.5)
ax2 = plt.subplot2grid((3,3), (1,0), colspan=2)
ax2.text(0.5,0.5,'subplot 2',ha='center',va='center',size=14,alpha=.5)
ax3 = plt.subplot2grid((3,3), (1,2), rowspan=2)
ax3.text(0.5,0.5,'subplot 3',ha='center',va='center',size=14,alpha=.5)
ax4 = plt.subplot2grid((3,3), (2,0))
ax4.text(0.5,0.5,'subplot 4',ha='center',va='center',size=14,alpha=.5)
ax5 = plt.subplot2grid((3,3), (2,1))
ax5.text(0.5,0.5,'subplot 5',ha='center',va='center',size=14,alpha=.5)
fig.tight_layout()
fig.tight_layout()
gridspec
fig = plt.figure()
gs = gridspec.GridSpec(2, 3, height_ratios=[2,1], width_ratios=[1,2,1])
for g in gs:
ax = fig.add_subplot(g)
fig.tight_layout()
Another example.
import matplotlib.gridspec as gridspec
G = gridspec.GridSpec(3, 3)
axes_1 = plt.subplot(G[0, :])
axes_1.set_xticks([]), axes_1.set_yticks([])
axes_1.text(0.5,0.5, 'Axes 1',ha='center',va='center',size=24,alpha=.5)
axes_2 = plt.subplot(G[1,:-1])
axes_2.set_xticks([]), axes_2.set_yticks([])
axes_2.text(0.5,0.5, 'Axes 2',ha='center',va='center',size=24,alpha=.5)
axes_3 = plt.subplot(G[1:, -1])
axes_3.set_xticks([]), axes_3.set_yticks([])
axes_3.text(0.5,0.5, 'Axes 3',ha='center',va='center',size=24,alpha=.5)
axes_4 = plt.subplot(G[-1,0])
axes_4.set_xticks([]), axes_4.set_yticks([])
axes_4.text(0.5,0.5, 'Axes 4',ha='center',va='center',size=24,alpha=.5)
axes_5 = plt.subplot(G[-1,-2])
axes_5.set_xticks([]), axes_5.set_yticks([])
axes_5.text(0.5,0.5, 'Axes 5',ha='center',va='center',size=24,alpha=.5)
add_axes
Manually adding axes with add_axes
is useful for adding insets to figures
fig, ax = plt.subplots()
ax.plot(xx, xx**2, xx, xx**3)
fig.tight_layout()
# inset
inset_ax = fig.add_axes([0.2, 0.55, 0.35, 0.35]) # X, Y, width, height
inset_ax.plot(xx, xx**2, xx, xx**3)
inset_ax.set_title('zoom near origin')
# set axis range
inset_ax.set_xlim(-.2, .2)
inset_ax.set_ylim(-.005, .01)
# set axis tick locations
inset_ax.set_yticks([0, 0.005, 0.01])
inset_ax.set_xticks([-0.1,0,.1]);