15

I'm using quadmesh to create a simple polar projection plot. Here's a minimal script which produces basically what I'm trying to do:

from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt

def make_plot(data,fig,subplot):
    nphi,nt = data.shape
    phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
    theta_coords = np.linspace(0,np.radians(35),nt+1)

    ax = fig.add_subplot(subplot,projection='polar')
    ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
    ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  

    theta,phi = np.meshgrid(phi_coords,theta_coords)
    quadmesh = ax.pcolormesh(theta,phi,data)
    ax.grid(True)
    fig.colorbar(quadmesh,ax=ax)
    return fig,ax


a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)
fig.savefig('test.png')

The above script creates a plot which looks like this:

enter image description here

I would like the colorbars to:

  1. Not overlap the 6 label.
  2. be scaled such that they are approximately the same height as the plot.

Is there any trick to make this work properly? (Note that this layout isn't the only one I will be using -- e.g. I might use a 1x2 layout, or a 4x4 layout ... It seems like there should be some way to scale the colorbar to the same height as the associated plot...)

mgilson
  • 300,191
  • 65
  • 633
  • 696

2 Answers2

29

This combination (and values near to these) seems to "magically" work for me to keep the colorbar scaled to the plot, no matter what size the display.

plt.colorbar(im,fraction=0.046, pad=0.04)
skytaker
  • 4,159
  • 1
  • 21
  • 31
16

You can do this with a combination of the pad, shrink, and aspect kwargs:

from __future__ import unicode_literals
import numpy as np
import matplotlib.pyplot as plt

def make_plot(data,fig,subplot):
    nphi,nt = data.shape
    phi_coords = np.linspace(0,np.pi*2,nphi+1) - np.pi/2.
    theta_coords = np.linspace(0,np.radians(35),nt+1)

    ax = fig.add_subplot(subplot,projection='polar')
    ax.set_thetagrids((45,90,135,180,225,270,315,360),(9,12,15,18,21,24,3,6))
    ax.set_rgrids(np.arange(10,35,10),fmt='%s\u00b0')  

    theta,phi = np.meshgrid(phi_coords,theta_coords)
    quadmesh = ax.pcolormesh(theta,phi,data)
    ax.grid(True)
    cb = fig.colorbar(quadmesh,ax=ax, shrink=.5, pad=.2, aspect=10)
    return fig,ax,cb


a = np.zeros((360,71)) + np.arange(360)[:,None]
b = np.random.random((360,71))
fig = plt.figure()
t1 = make_plot(a,fig,121)
t2 = make_plot(b,fig,122)

figure.colorbar doc

The best value for these parameters will depend on the aspect ratio of the axes.

The size of the axes seems to not get shrink-wrapped to the polar plot, thus in the 1x2 arrangement there is a lot of space above and below the plot that are part in the axes object, but empty. The size of the color bar is keyed off of the rectangular size, not the round size, hence why the default values are not working well. There is probably a way to do the shrink-wrapping, but I do not know how to do that.

An alternate method is to force your figure to be the right aspect ratio ex:

fig.set_size_inches(10, 4) # for 1x2
fig.set_size_inches(4, 10) # for 2x1

which makes the sub plots square, so the default values more-or-less work.

tacaswell
  • 84,579
  • 22
  • 210
  • 199
  • This works (more or less) for the 2x1 layout, but not for a 1x2 layout (then the colorbar becomes way too small). I'm really looking for a layout independent solution. As far as my rcparams go, I'm not sure. I don't have any rc file (to my knowledge) and `print matplotlib.rcParams['bounding_box_tight']` results in a `KeyError`. – mgilson May 22 '13 at 23:51
  • @mgilson I have the key remembered wrong, see edit. I think the fundamental problem is that the `axes` object's bounding box does not shrink wrap around the polar plot and all the colorbar lengths are keyed off of the over-sized bounding box. – tacaswell May 23 '13 at 00:13
  • I still get a `KeyError` for `savefig.bbox`. I even tried `[v for v in matplotlib.rcParams.values() if isinstance(v,str) and 'tight' in v]` which gave me an empty list. I'm using version 1.1.1. – mgilson May 23 '13 at 00:27
  • @mgilson On further thought, I think the bonding box thing was a blind alley, sorry. – tacaswell May 23 '13 at 01:35