Minor Tick Labels in Matplotlib

This is a slightly more technical post than usual but having figured out how to do something quite esoteric in Matplotlib I thought I would write it down to save me remembering.

I have been making quite a few plots recently for a paper which should hit the arXiv very soon. The Python plotting package Matplotlib has been indispensable in this regard, especially as I took the effort of creating a script which creates all the plots. This meant that redoing all the graphs for new results or with changed sizes etc. was a simple as rerunning the script.

Quite a few of the plots use log axes and while Matplotlib performs admirably there was one problem I had with certain plots. By default, the log plots only show tick labels for each order of magnitude. Tick labels are the numbers on the x or y axis telling you the corresponding numerical value, and when the figure is zoomed in it is possible to lose the major tick label at say 10-4 because you only want to plot values from 0.3×10-4 and 0.5×10-4. Obviously this removes all sense of scale from the plot. A very mediocre solution is to just zoom out until a major tick label is back in the plot but this is obviously unsatisfactory.

I looked through the Matplotlib documentation, which has very detailed information about the API and has a lot of examples, but unfortunately didn’t address this exact point. After a bit of searching I found a useful conversation on the users mailing list which got me close but didn’t use the LaTeX labels which are really essential for publication quality graphs (in my opinion anyway!). The tick labels documentation along with the major-minor ticks example led me to the Formatter classes, especially LogFormatter and LogFormatterMathtext. This looked like the right answer but unfortunately LogFormatterMathtext writes the minor tick labels in a very unusual way. Instead of 0.3×10-4 it only writes an exponent, so 10-4.52.

I finally settled on extending the pyplot.LogFormatter class which controls the text for the tick labels. My subclass is as follows:

import re
import pylab

class LogFormatterTeXExponent(pylab.LogFormatter, object):
    """Extends pylab.LogFormatter to use 
    tex notation for tick labels."""
    
    def __init__(self, *args, **kwargs):
        super(LogFormatterTeXExponent, 
              self).__init__(*args, **kwargs)
        
    def __call__(self, *args, **kwargs):
        """Wrap call to parent class with 
        change to tex notation."""
        label = super(LogFormatterTeXExponent, 
                      self).__call__(*args, **kwargs)
        label = re.sub(r'e(\S)0?(\d+)', 
                       r'\\times 10^{\1\2}', 
                       str(label))
        label = "$" + label + "$"
        return label

It is provided as is, but there shouldn’t be too much wrong with it. One odd thing is that the LogFormatter class is an old style class, so I inherited from object to make it my subclass a new style class. This might be dangerous and cause some unexpected problems.

To use the class you can do something like the following:

import pylab
import numpy as np

fig = pylab.figure()
pylab.semilogy(np.logspace(-6,-5))
ax = fig.gca()
ax.yaxis.set_minor_formatter(
    LogFormatterTeXExponent(base=10, 
     labelOnlyBase=False))
pylab.draw()

Below are three different figures showing the current default situation, the result of using LogFormatterMathtext and the result of the new class. I hope this will be of use to someone who has been struggling with this problem as I have.

As I mentioned, this came up because of a paper that is very nearly completed and should be available soon. Along with the paper we should have the long promised release of the code I have been working on which solves cosmological perturbation equations during inflation. More on that soon.

 

Ian

A physicist by training, I am curious about the world around us, from the smallest to the largest scales. I am now a part of the Pivotal Data Science team and work on interesting data science and predictive analytics projects across a wide range of industries. On Twitter I'm @ianhuston, and on Github I'm ihuston.

 

Leave a Reply

Your email address will not be published. Required fields are marked *

Bear