12

I'd like to draw a (vertical) colorbar, which has two different scales (corresponding to two different units for the same quantity) on each side. Think Fahrenheit on one side and Celsius on the other side. Obviously, I'd need to specify the ticks for each side individually.

Any idea how I can do this?

andreas-h
  • 10,059
  • 17
  • 56
  • 72

3 Answers3

13

That should get you started:

import matplotlib.pyplot as plt
import numpy as np

# generate random data
x = np.random.randint(0,200,(10,10))
plt.pcolormesh(x)

# create the colorbar
# the aspect of the colorbar is set to 'equal', we have to set it to 'auto',
# otherwise twinx() will do weird stuff.
cbar = plt.colorbar()
pos = cbar.ax.get_position()
cbar.ax.set_aspect('auto')

# create a second axes instance and set the limits you need
ax2 = cbar.ax.twinx()
ax2.set_ylim([-2,1])

# resize the colorbar (otherwise it overlays the plot)
pos.x0 +=0.05
cbar.ax.set_position(pos)
ax2.set_position(pos)

plt.show()

enter image description here

hitzg
  • 11,442
  • 47
  • 53
  • You don't need to resize the colorbar, just set axisbelow to False: cbar.ax.set_axisbelow(False) # 1. axis ax2 = cbar.ax.twinx() ax2.set_ylim([low,high]) # low high for limts ax2.set_axisbelow(False) # 2. axis – Andreas Jul 17 '15 at 13:41
  • @Andreas I don't understand what you mean: [`set_axisbelow`](http://matplotlib.org/api/axes_api.html#matplotlib.axes.Axes.set_axisbelow) has no influence on the size or position of the axes instance. – hitzg Jul 17 '15 at 13:51
  • Ahh, I see what you mean, I thought it was to offset the 2. axis - so just ignore this in your context, sorry for the confusion – Andreas Jul 21 '15 at 11:00
9

If you create a subplot for the colorbar, you can create a twin axes for that subplot and manipulate it like a normal axes.

import matplotlib.colors as mcolors
import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-1,2.7)
X,Y = np.meshgrid(x,x)
Z = np.exp(-X**2-Y**2)*.9+0.1

fig, (ax, cax) = plt.subplots(ncols=2, gridspec_kw={"width_ratios":[15,1]})

im =ax.imshow(Z, vmin=0.1, vmax=1)
cbar = plt.colorbar(im, cax=cax)
cax2 = cax.twinx()

ticks=np.arange(0.1,1.1,0.1)
iticks=1./np.array([10,3,2,1.5,1])
cbar.set_ticks(ticks)
cbar.set_label("z")
cbar.ax.yaxis.set_label_position("left")
cax2.set_ylim(0.1,1)
cax2.set_yticks(iticks)
cax2.set_yticklabels(1./iticks)
cax2.set_ylabel("1/z")

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 289,005
  • 45
  • 571
  • 615
0

I am using an inset axis for my colorbar and, for some reason, I found the above to answers no longer worked as of v3.4.2. The twinx took up the entire original subplot.

So I just replicated the inset axis (instead of using twinx) and increased the zorder on the original inset.

axkws = dict(zorder=2)
cax = inset_axes(
    ax, width="100%", height="100%", bbox_to_anchor=bbox, 
    bbox_transform=ax.transAxes, axes_kwargs=axkws
)
cbar = self.fig.colorbar(mpl.cm.ScalarMappable(cmap=cmap), cax=cax)
cbar.ax.yaxis.set_ticks_position('left')
    
caxx = inset_axes(
    ax, width="100%", height="100%", 
    bbox_to_anchor=bbox, bbox_transform=ax.transAxes
)
caxx.yaxis.set_ticks_position('right')
Ryan Skene
  • 734
  • 1
  • 11
  • 26