Tag Archives: matplotlib

Reducing the padding in matplotlib figures

By default, matplotlib figures have a lot of padding around them. This is quite annoying when producing plots for publication, as you end up with a load of useless whitespace that you have to compensate for by screwing around with the figure sizing and position (especially in LaTeX articles).

Fortunately, matplotlib has a neat convenience function that removes all of this unnecessary whitespace in one fell swoop. It’s as easy as this:

plt.tight_layout()

See the figures below for a comparison. This function also takes a ‘pad’ argument that lets you fine-tune the padding manually.

Oh, how I wish I’d known about this three years ago.

Matplotlib figure with normal padding

Normal padding

Matplotlib figure with minimal padding

Minimal padding


Customising contour plots in matplotlib

So, you need to include a contour plot in some publication of yours, little one? There are two things that you must learn. But beware! The first will raise your spirits, while the second will quicken your descent into madness. These facts I address to you, should you stand to read them: (1) matplotlib has a really customisable contour plot implementation; (2) the convenience functions are few, and the necessary keyword arguments are confusing.

I’m prepping a bunch of contour plots for a publication in a quick letter at the moment, and want to improve their legibility. This involves things like making all of the lines thicker, increasing the font size of the labels (both on the axes and on the contours themselves), and changing the contour scaling. Some of these modifications have proven more difficult than others with matplotlib, so I thought I’d jot down a couple of examples here for reference. (The matplotlib contour examples are useful, but don’t cover everything.)

Tick size

First of all, let’s change the width and length of the ticks on each axis, and the size of the font of the labels for each tick.

import pylab as P

MP_LINEWIDTH = 2.4
MP_TICKSIZE = 10.

P.rc('axes', linewidth=MP_LINEWIDTH)

P.subplot(111)
for tick in P.gca().xaxis.get_major_ticks():

  tick.label1.set_fontsize(20.)
  tick.tick1line.set_markeredgewidth(MP_LINEWIDTH)
  tick.tick2line.set_markeredgewidth(MP_LINEWIDTH)
  tick.tick1line.set_markersize(0.5*MP_TICKSIZE)
  tick.tick2line.set_markersize(0.5*MP_TICKSIZE)

The call to P.rc() changes the thickness of all of the axis lines on the plot (i.e. it changes the global properties, and isn’t restricted to just one plot). The other stuff could probably be done using calls to rc(), but that’s an exercise for another time.

Plot of Compton y-distortion.

An example of a customised contour plot in matplotlib.

The loop is over all major ticks on the x axis of the current subplot. (The call to gca() gets the current set of plot axes, but of course you could use the xaxis.get_minor_ticks() method from any previously-defined axes object.) The object tick1line is the x-axis at the bottom of the plot, and tick2line is at the top.

Inside the loop, I’m setting the font size of the tick label (the number that appears below each tick), the width of the tick (using the markeredgewidth property), and the length of the tick (using markersize). You can also do things like hiding certain ticks, hiding certain labels (especially useful if you want to remove the tick label at the origin, because it overlaps with the label for the other axis there), changing the appearance of gridlines (somewhat unintuitively), and so on. There’s a list of all the tick-related objects that you can change here.

This will only change the tick styles for the minor ticks on the x axis. To cover all of the ticks on the plot, you’ll need to loop through all the minor ticks using P.gca().xaxis.get_minor_ticks() too, and then do both major and minor ticks for the y axis (using P.gca().yaxis) as well.

Changing which values contours are drawn at, and how they are labeled

To change where the contours are drawn, you need to change the contour locator. This is a keyword argument to contour(), and requires a ticker object. There’s a list of built-in tickers here. In the example below, I wanted a log scaling for my plots, so I used LogLocator().

It’s also useful to be able to change the labels that are added to the contours.For this, you need to use the clabel() function (which controls contour labels) and manipulate the formatter, using the fmt keyword. A list of formatters is given here, with more documentation on them (including formatter-specific keyword arguments for further customisation) given further down that page. Particularly useful formatters include:

  • LogFormatterMathtext (which add LaTeX mathmode labels, especially useful for numbers with exponents)
  • FormatStrFormatter (which lets you use a C-like format string to determine how significant figures, signs, etc. are displayed)
  • FuncFormatter (which lets you define a custom function to handle string formatting)

Setting the font size of the contour label is as easy as changing the fontsize keyword of clabel().

Finally, a subtlety of contour() is its use of the linewidths keyword, rather than linewidth (as with other pylab functions). It works exactly the same otherwise.

from matplotlib import ticker
ctr = P.contour(X, Y, Z, locator=ticker.LogLocator(), colors='k', linewidths=3.0)
P.clabel(ctr, inline=1, fontsize=20., fmt=ticker.LogFormatterMathtext())


Recent numpy/matplotlib finds: Log plotting and map projections

With a change of research focus comes a change of tools. I’m fiddling with clusters of galaxies at the moment (GR stuff has been put slightly to one side for a little while), so I’ve been re-educating myself in the art of handling catalogues. I discovered a handful of neat numpy/matplotlib routines in the process, which I thought I’d share:

  • Map projections: You can project plots differently by using the projection keyword argument for subplot, e.g. subplot(111, projection="mollweide")
  • Logarithmic sampling: If you need to sample some function on a logarithmic interval (e.g. a cluster pressure profile), use numpy’s logspace function instead of the usual linspace.
  • Split log axes: Sometimes you want log axes for a plot that includes both positive and negative values. Rather than messing around with taking the absolute value of the negative numbers and then changing the line style, you can use SymmetricalLogScale.

Drawing arrows in Matplotlib

This is a fun one. A colleague of mine spent a while this afternoon trying to get arrows to work in Matplotlib, so that he could draw upper-limit points on a graph (similar to this). All to no avail – the arrow() function would happily draw lines, but without an arrow head. He came to ask about it, and we spent a little while digging into the documentation to see what was up.

As it turns out, the documentation was what’s up. The function reference for arrow() is missing information about some rather important keyword arguments, the ones that control arrow head presentation. Even worse, in the versions of Matplotlib we’re using, the default option seems to be to display no arrow head at all! This is clearly broken – arrows should look like arrows by default.

Fortunately, there were a couple of code examples available on the Matplotlib website. Unfortunately, one was rather complicated (312 lines of code!), and the other wasn’t really what he was looking for (fancy arrows in LaTeX). We ended up trying to reverse-engineer the first example, which is when we stumbled upon the extra, undocumented, keyword arguments that would make everything work in a nice, easy way. These are head_length and head_width, which need to be set to non-zero float values to display an arrow head.

I’ve included a minimal example below – it just draws a black arrow with a sensibly-proportioned arrow head. There are other arrow style keywords that you can use too; look at the ArrowStyle documentation to see them listed.

import pylab as P
P.subplot(111)
# P.arrow( x, y, dx, dy, **kwargs )
P.arrow( 0.5, 0.8, 0.0, -0.2, fc="k", ec="k",
head_width=0.05, head_length=0.1 )
P.show()

Drawing an arrow in Matplotlib


Custom dash/dot line styles in matplotlib

I’ve recently been polishing a number of graphs to get them up to publication quality (coming soon!), and one of the necessary customisations that came up was the style of dashed/dotted lines. Matplotlib’s default dotted and dash/dotted line styles aren’t very clear in a lot of plots, and can be difficult to distinguish (e.g. the dash/dotted style looks too much like dots).

Happily, you can make your own custom line styles, using a sequence (list) of numbers and the set_dashes() method of the Line2D object. To do this:

import pylab as P
P.subplot(111)
line = P.plot(x, y) # 'line' is actually a list of lines. We want line[0]
seq = [2, 4, 7, 4]
line[0].set_dashes(seq)

Here, seq is the alternating sequence of dashes and spaces. seq[0] draws a line of length 2, seq[1] draws a space (i.e. no line) of length 4, seq[2] a line of length 7, and seq[3] a space of length 4.

You can carry on this sequence indefinitely if you like; even indices are drawn as lines, odd indices as spaces. For example, seq = [2, 2] would look like dots, and seq = [7, 4, 3, 4] is a clearer dash-dotted line than the default. You can even use float lengths for the dashes, rather than integers.

(There’s a rather spartan example on the matplotlib website too.)