part of Course 133 Navigating Matplotlib
Tip-Top Tick Tips and Tricks
Well formatted axis ticks elevate a plot from serviceable to professional, but unfortunately it’s one of the toughest things to do in Matplotlib. This recipe collection is designed to change that.
There are at least three different ways to perform most tick formatting operations. I’ve chosen to stick with the set that feels the most consistent, but don’t be surprised when you come across lots of alternatives on StackOverflow.
All the examples to follow will rely on this bit of setup. (See the Level Up post for a line-by-line breakdown.)
import matplotlib
matplotlib.use("agg")
import matplotlib.pyplot as plt
import numpy as np
# Create the curve data.
x = np.linspace(1, 13, 500)
y = 1 + np.sinc(x - 7)
fig = plt.figure()
ax = fig.gca()
ax.plot(x, y)
Impose axis limits
Setting axis limits is a good place to start. By default, Matplotlib uses the range of the data to choose reasonable limits. This is good enough most of the time, but occasionally you will want more control. You may want to avoid suppressing your zero or to ensure that multiple plots have the same scale or to drive a certain aspect ratio, or maybe you just like having the limits fall on even whole numbers. Whatever the reason, specifying limits gives you a greater degree of control over the framing of your plot.
ax.set_xlim(x_minimum, x_maximum)
ax.set_ylim(y_minimum, y_maximum)
This is thankfully one of the most self-explanatory examples in the bunch.
Use logarithmic scaling
Some data lends itself well to logarithmic scaling on one of its axes, or both. Matplotlib handles that transformation gracefully.
ax.set_xscale("log")
ax.set_yscale("log")
It even adds a second set of smaller ticks without labels, called minor ticks, to help the viewer resolve locations. Minor ticks will be showing up again in later examples.
Put ticks inside and out
Fine control over ticks can make the story your plot tells crisper. By default they sit just outiside the plot frame, but you can place the ticks inside the frame or half in, half out.
ax.tick_params(axis="x", direction="in")
ax.tick_params(axis="y", direction="inout")
To adjust this aspect of tick behavior, we get to use
tick_params()
. With the direction
argument
we can specify in
, out
, or
inout
to get the precise placement we want.
Also, note
how we can specify the axis
we want to adjust. The
default is both
and when it's in force, anything we do
will affect both x- and y-axes. When we want to adjust them
separately, we can call them out individually, as we did here.
Another power that we get with tick_params()
is that everything we do with major ticks can also be done with minor ticks.
The which
argument lets us select between them.
By default, it only operates on major
ticks, but we
could also call out which="minor"
to perform the same
operation on our minor ticks.
ax.tick_params(which="minor", "axis="x", direction="in")
ax.tick_params(which="minor", axis="y", direction="inout")
There is no inherent difference between major and minor ticks, but it's often convenient to have two sets. One might be larger, with labels and wider spacing, the other finer. By convention these are called major and minor, but the distinction ends there as far as Matplotlib is concerned.
The tick_params()
function will come up a lot in the
next few examples. If you want to take a closer look at the API to
get a full sense of its abilities,
you can find it here.
Put ticks on the top and right axes
By default, ticks and tick labels will be added to the left and
bottom axes. To make changes to this, you can use
tick_params()
to turn ticks and tick labels on and off
for each axis independently.
ax.tick_params(bottom=False, top=True, left=True, right=True)
ax.tick_params(labelbottom=False, labeltop=True, labelleft=True, labelright=True)
The boolean arguments bottom
, top
,
right
, and left
determine whether the ticks
are shown. As you might have guessed, tick labels are similarly
controlled using labelbottom
, labeltop
,
labelleft
, and labelright
. You can have one without the other, both, or
neither on any axis in any
combination.
Stylize the ticks
Sometimes if your taste is particular, the default ticks just aren't quite right. Luckily you can adjust the length, width, and color of them as well.
ax.tick_params(axis="x", direction="in", length=16, width=2, color="turquoise")
ax.tick_params(axis="y", direction="in", length=6, width=4, color="orange")
The tick_params
accepts these parameters with
length
and width
measured in points and
color
any valid color, as in the lesson on setting line color.
Tweak the label style
The lesson on text showed how to customize axis labels and plot text in a myriad of ways, but stopped short of showing how to handle tick labels. Now we finally get to that.
ax.tick_params(axis="x", labelsize=18, labelrotation=-60, labelcolor="turquoise")
ax.tick_params(axis="y", labelsize=12, labelrotation=20, labelcolor="orange")
We don't get quite the freedom with tick labels as we do with other text, but we get enough to make things interesting. Size, rotation, and color are all modifiable.
As you can see, this gives you a fair amount of power over your tick
labels. If you need to go further than that, like changing the font
or italicizing it, that's beyond the scope of this post. If you
decide to turn to StackOverflow for this, a word of caution: it's a
deep rabbit hole. My personal recommendation is to use
text()
and hand place them. It's a bit extreme, but
after 2 hours of trial and error when you're neck deep in Matplotlib
source code, you'll see why.
As a side note, notice how the x-label no longer fits on the graph after growing the x-axis tick labels. That's because, when the Axes are first created, Matplotlib makes a reasonable guess at how much space the ticks and axis labels are going to take and places the plot accordingly. This is a great illustration of how sometimes we will need to take the Axes layout into our own hands. That will be to topic of a forthcoming post.
Add minor ticks
We got minor ticks for free when we log scaled our axis, but if we want to add them on linear scales, we can do that too.
from matplotlib.ticker import AutoMinorLocator
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_locator(AutoMinorLocator())
To do this we have to introduce the notion of a Locator.
It's a function that generates the locations according to some rules.
There are several different types, but Auto locators are a great
place to start. They automatically create a reasonable set of ticks. By default, major ticks are initialized with an Auto
locator, but minor ticks aren't. To turn them on, we have to import
the AutoMinorLocator
and use it to set minor ticks for
the x- and y-axes.
Add labels to the minor ticks
Another powerful tick tool is a Formatter. It's a function that lets you transform the tick label to be an integer, a fixed-place decimal, or lots of other things.
from matplotlib.ticker import AutoMinorLocator, FormatStrFormatter
ax.xaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_locator(AutoMinorLocator())
ax.yaxis.set_minor_formatter(FormatStrFormatter("%.3f"))
Here, we imported the FormatStrFormatter
, and used it to
create tick label strings using the coordinates of each tick.
The helpful package that contains all the Formatters and Locators is
called
ticker
.
If you are curious about all the Formatters and Locators you have at
your disposal, I recommend the
formatter gallery and the
locator gallery.
Exercise total tick control
When you have to do some tick gymnastics that are at all
non-standard, the FixedLocator
and
FixedFormatter
are your friends. These let you hard
code in every tick location and tick label.
from matplotlib.ticker import FixedLocator, FixedFormatter
x_formatter = FixedFormatter([
"I like this spot", "and this", "and that", "but not this one."])
y_formatter = FixedFormatter(["-1e7", "111", "007", "xkcd"])
x_locator = FixedLocator([3, 7, 8.8, 12])
y_locator = FixedLocator([.85, 1.15, 1.28, 1.9])
ax.xaxis.set_major_formatter(x_formatter)
ax.yaxis.set_major_formatter(y_formatter)
ax.xaxis.set_major_locator(x_locator)
ax.yaxis.set_major_locator(y_locator)
Supplying a list of positions to FixedLocator
and a list
of labels of the same length to
FixedFormatter
lets you arbitrarily specify the position
and label of each tick.
You can immediately see the power that this bestows on you. Your axes can say whatever you want. Just remember that with great tick formatting power comes great responsibility. If you ever change your axes to intentionally misrepresent your data, you will be kicked out of data science club, no backsies.
Add a custom grid
The cousin of the tick is the grid, tick lines that cross the entire
plot.
Matplotlib makes it easy to create one and supplies us with a
grid()
command.
ax.grid(axis="x", color="green", alpha=.3, linewidth=2, linestyle=":")
ax.grid(axis="y", color="black", alpha=.5, linewidth=.5)
Although it defaults to making both x- and y-grid lines the same,
grid()
also handles them individually, as in our code here.
It takes all the arguments that we could pass to plot()
when formatting a line.
After the complexity of ticks, grids offer a straightforward way to make our plots stylish and readable. To dig deeper into the options available, check out the grid API.
You now have mastery of ticks. I hope it sets you up well to make your plots look polished with a minimum of internet searching. There are a handful of other Matplotlib how-to's you can refer to: come take a look at the full set of tutorials.