4

I am trying to add a colorbar to a chart that has a secondary y-axis. A minimal-ish example of my difficulties is given below. The trouble is that I cannot add a colorbar to an AxesSubplot object, and adding it directly as plt.colorbar puts it on top of my graph- see how it overlaps the rightmost y-axis. How can I fix this? Cheers.

import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate

time=[]
temp=[]
temp_f=[]
height=[]
for ti in np.arange( 0, 10, 0.5 ):
    for te in np.arange( -12, 12, 0.5 ):
        height.append( np.sqrt(1+ti)/(1+te*te)+ti*te/12.5 )
        time.append(ti)
        temp.append(te)
        temp_f.append( 32+9.0*te/5)

time=np.array(time)
temp=np.array(temp)
temp_f=np.array(temp_f)
height=np.array(height)

xi, yi = np.linspace(time.min(), time.max(),25), np.linspace(temp.min(), temp.max(), 25)
xi, yi = np.meshgrid(xi, yi)

rbf = scipy.interpolate.Rbf(time, temp, height, function='linear')
zi = rbf(xi, yi)

fig = plt.figure()
ax1 = plt.gca()
ax1.set_xlabel( "Time (s)" )
ax1.set_ylabel( "Celsius" )

ax2 = ax1.twinx()
ax2.set_ylabel( "Fahrenheit" )
ax2.set_ylim( [temp_f.min(), temp_f.max()] )

img = ax1.imshow( zi, vmin=height.min(), vmax=height.max(), origin='lower', 
       extent=[time.min(), time.max(), temp.min(), temp.max()],
       aspect='auto', cmap='YlOrRd')

plt.colorbar(img, label=r"Height (cm)",format='%1.1f', ax=ax1)

plt.show()
TheBigH
  • 524
  • 3
  • 15

2 Answers2

3

See positioning the colorbar.

You can manually specify the location of the colorbar by:

cbaxes = fig.add_axes([1, 0.15, 0.03, 0.7])
plt.colorbar(img, label=r"Height (cm)",format='%1.1f', ax=ax1, cax=cbaxes)
Community
  • 1
  • 1
yangjie
  • 6,619
  • 1
  • 33
  • 40
2

plt.colorbar does a good job for simple plots with one axes. With more complex setups it can get confused. So it is better to manually setup the axes for the colorbar. You can use the subplots function for that. By supplying gridspec_kw you can tell the underlying GridSpec object how you want the widths to be specified:

fig, [ax1, cax] = plt.subplots(1,2, gridspec_kw=dict(width_ratios=[10,1]))

when creating the colorbar you want to specify the axes which should be used to create the colorbar in:

plt.colorbar(img, label=r"Height (cm)",format='%1.1f', ax=ax1, cax=cax)

So with the full code:

import matplotlib.pyplot as plt
import numpy as np
import scipy.interpolate

time=[]
temp=[]
temp_f=[]
height=[]
for ti in np.arange( 0, 10, 0.5 ):
    for te in np.arange( -12, 12, 0.5 ):
        height.append( np.sqrt(1+ti)/(1+te*te)+ti*te/12.5 )
        time.append(ti)
        temp.append(te)
        temp_f.append( 32+9.0*te/5)

time=np.array(time)
temp=np.array(temp)
temp_f=np.array(temp_f)
height=np.array(height)

xi, yi = np.linspace(time.min(), time.max(),25), np.linspace(temp.min(), temp.max(), 25)
xi, yi = np.meshgrid(xi, yi)

rbf = scipy.interpolate.Rbf(time, temp, height, function='linear')
zi = rbf(xi, yi)

fig, [ax1, cax] = plt.subplots(1,2, gridspec_kw=dict(width_ratios=[10,1]))
ax1.set_xlabel( "Time (s)" )
ax1.set_ylabel( "Celsius" )

ax2 = ax1.twinx()
ax2.set_ylabel( "Fahrenheit" )
ax2.set_ylim( [temp_f.min(), temp_f.max()] )

img = ax1.imshow( zi, vmin=height.min(), vmax=height.max(), origin='lower', 
       extent=[time.min(), time.max(), temp.min(), temp.max()],
       aspect='auto', cmap='YlOrRd')

plt.colorbar(img, label=r"Height (cm)",format='%1.1f', ax=ax1, cax=cax)

plt.show()

You get:

enter image description here

hitzg
  • 12,133
  • 52
  • 54
  • Another excellent suggestion. I'd just add that, if you want more space between the axis and the colorbar I can do fig, [ax1, dummy, cax] = plt.subplots(1,2, gridspec_kw=dict(width_ratios=[10,1,1])), and dummy.axis('off') to get more blank space. – TheBigH Jun 30 '15 at 16:19
  • 1
    Actually, there is a proper way to do that (without the hack of adding unused axes): use the `wspace` argument: `[ax1, cax] = plt.subplots(1,2, gridspec_kw=dict(width_ratios=[10,1], wspace=0.1))` – hitzg Jun 30 '15 at 18:49
  • Also note that with the approach in the accepted answer you have full control of the size and placing of the colorbar. This also means that the height (and vertical positioning) doesn't automatically match the one of the `ax1`, but needs to be tuned manually as well. – hitzg Jun 30 '15 at 18:50