Wednesday, November 10, 2010

Working with a second y-axis in matplotlib

Recently I had to create a plot with data on a second y-axis in matplotlib (python). This is actually more involved than I expected. After googling around I put this together

First you store the axes object returned from a subplot(111) command

ax1=subplot(111)

then you plot as usual your data which has to go to the first y-axis. Labels you have to set for the ax1 object:

ax1.set_xlabel(r"Position ($\mu$m)",fontsize=20)
ax1.set_ylabel("Concentration (wt %)",fontsize=20)

The second y-axis is created by creating a twin of your current axes object. The twin is actually not identical but has a mirrored y-axis (i.e. your second y-axis).

ax2=twinx()
plot(....)
ax2.set_label("Other conc (wt%)",fontsize=20)

So far so good. Normally I use the follow function to change the size of text of the ticks


def setTickSize(size=None):
 """Set the tick label size (both x and y axis) to the specified value or 
 default to 'large'
 possible values are 'small', 'medium', 'larger', 'x-large', 'xx-large'
 'x-small', 'xx-small' or integer values
 !!!!!!!!Warning!!!!!!!!!
 Use this function only when you are finished adding curves. If curves
 change the axes ranges the ticks get screwed up (sorry)
 """

 if size is None:  #default large
  size='large'
 elif type(size) == str:
  if size not in ['small', 'medium', 'larger', 'x-large', 'xx-large','x-small', 'xx-small']:
   print "Wrong fontsize specifier given, use:"
   print 'small', 'medium', 'larger', 'x-large', 'xx-large', 'x-small', 'xx-small'
   print 'defaulting to large'
   size='large'
 elif type(size) != int:
  print "Wrong fontsize specifier given, use:"
  print 'small', 'medium', 'larger', 'x-large', 'xx-large', 'x-small', 'xx-small'
  print 'or an integer value'
  print 'defaulting to large '
  size='large'
 
 tmp=pylab.xticks()
 pylab.xticks(tmp[0],[str(int(x)) for x in tmp[0]], size=size)
 tmp=pylab.yticks()
 pylab.yticks(tmp[0],[str(int(x)) for x in tmp[0]], size=size)
You have to change the type cast int(x) into something else if you want floating point values on your axes. However, it turns out that it is near impossible to do this for the ticks on the second y-axis. After some more googling I found a much easier alternative. Matplotlib can read settings from a user-preferences file somewhere in the user-home directory. Somehow I can never find this file (I use a lot of different windows/linux/python combinations). But from a script you can also set these preferences! Which of course then only apply to the remainder of the script:

import matplotlib as mpl
mpl.rcParams['xtick.labelsize']=20
mpl.rcParams['ytick.labelsize']=20

The nice part is that the labelsize applies to both your first and second y-axis. You can find more info on the matplotlibrc file on the sourceforge matplotlib site

The only issue I could not solve is to create one legend for the curves on both axes. If you legend() you only get the legend for the current active axes. On windows calling it again just replaced the other legend on linux they were both displayed on top of eachother (which gives you the option to use e.g. legend(loc=2) for the left y-axis and legend(loc=1) for right y-axis).

4 comments:

  1. Were you able to find solution for legend problem
    ?

    ReplyDelete
  2. No unfortunately I never found an easy way to get a combined legend

    ReplyDelete
  3. Can anyone tell me how to draw multiple Y axis(eg 5) with different scales in matplolib.??

    ReplyDelete
    Replies
    1. More than two Y axes does not seem to be supported by matplotlib (see http://matplotlib.org/faq/howto_faq.html#multiple-y-axis-scales)

      Delete