Question

I would like to position a colorbar inside a scatter plot by specifying the position in data coordinates. Here is an example of how it works when specifying figure coordinates:

import numpy as np
import matplotlib.pyplot as plt    

#Generate some random data:
a = -2
b = 2
x = (b - a) * np.random.random(50) + a
y = (b - a) * np.random.random(50) + a
z = (b) * np.random.random(50)

#Do a scatter plot
fig = plt.figure()
hdl = plt.scatter(x,y,s=20,c=z,marker='o',vmin=0,vmax=2)
ax = plt.gca()
ax.set_xlim([-2,2])
ax.set_ylim([-2,2])

#Specifying figure coordinates works fine:
fig_coord = [0.2,0.8,0.25,0.05]
cbar_ax = fig.add_axes(fig_coord)

clevs = [0, 1 , 2]
cb1 = plt.colorbar(hdl, cax=cbar_ax, orientation='horizontal', ticks=clevs)

plt.show()

...Ok, can't include an image of the plot here because I am lacking reputation. But the above code will give you an impression....

Now the question is, how could I position the colorbar at data coordinates, to appear at e.g.: left, bottom, width, height: -1.5, 1.5, 1, 0.25

I have experimented with a few things, like determining the axes position within the figure and transforming it to data coordinates but didn't succeed.

Many thanks for ideas or pointing me to already answered similar questions!

Here is what I did (not particularly beautiful but it helps). Thanks tcaswell !

#[lower left x, lower left y, upper right x, upper right y] of the desired colorbar:
dat_coord = [-1.5,1.5,-0.5,1.75]
#transform the two points from data coordinates to display coordinates:
tr1 = ax.transData.transform([(dat_coord[0],dat_coord[1]),(dat_coord[2],dat_coord[3])])
#create an inverse transversion from display to figure coordinates:
inv = fig.transFigure.inverted()
tr2 = inv.transform(tr1)
#left, bottom, width, height are obtained like this:
datco = [tr2[0,0], tr2[0,1], tr2[1,0]-tr2[0,0],tr2[1,1]-tr2[0,1]]
#and finally the new colorabar axes at the right position!
cbar_ax = fig.add_axes(datco)
#the rest stays the same:
clevs = [0, 1 , 2]
cb1 = plt.colorbar(hdl, cax=cbar_ax, orientation='horizontal', ticks=clevs)

plt.show()
Was it helpful?

Solution

Here is what I did, based on the comments to my original question:

import numpy as np
import matplotlib.pyplot as plt    

a = -2
b = 2

x = (b - a) * np.random.random(50) + a
y = (b - a) * np.random.random(50) + a
z = (b) * np.random.random(50)

fig = plt.figure()
hdl = plt.scatter(x,y,s=20,c=z,marker='o',vmin=0,vmax=2)
ax = plt.gca()
ax.set_xlim([-2,2])
ax.set_ylim([-2,2])

#[(lower left x, lower left y), (upper right x, upper right y)] of the desired colorbar:
dat_coord = [(-1.5,1.5),(-0.5,1.75)]
#transform the two points from data coordinates to display coordinates:
tr1 = ax.transData.transform(dat_coord)
#create an inverse transversion from display to figure coordinates:
inv = fig.transFigure.inverted()
tr2 = inv.transform(tr1)
#left, bottom, width, height are obtained like this:
datco = [tr2[0,0], tr2[0,1], tr2[1,0]-tr2[0,0],tr2[1,1]-tr2[0,1]]
#and finally the new colorabar axes at the right position!
cbar_ax = fig.add_axes(datco)
#the rest stays the same:
clevs = [0, 1 , 2]
cb1 = plt.colorbar(hdl, cax=cbar_ax, orientation='horizontal', ticks=clevs)

plt.show()

OTHER TIPS

Two step to specify the position in data coordinates of an Axes:

  1. use Axes.set_axes_locator() to set a function that return a Bbox object in figure coordinate.
  2. set the clip box of all children in the Axes by set_clip_box() method:

Here is the full code:

import numpy as np
import matplotlib.pyplot as plt    

#Generate some random data:
a = -2
b = 2
x = (b - a) * np.random.random(50) + a
y = (b - a) * np.random.random(50) + a
z = (b) * np.random.random(50)

#Do a scatter plot
fig = plt.figure()
hdl = plt.scatter(x,y,s=20,c=z,marker='o',vmin=0,vmax=2)
ax = plt.gca()
ax.set_xlim([-2,2])
ax.set_ylim([-2,2])

#Specifying figure coordinates works fine:
fig_coord = [0.2,0.8,0.25,0.05]
cbar_ax = fig.add_axes(fig_coord)

def get_ax_loc(cbar_ax, render):
    from matplotlib.transforms import Bbox
    tr = ax.transData + fig.transFigure.inverted()
    bbox = Bbox(tr.transform([[1, -0.5], [1.8, 0]]))
    return bbox

clevs = [0, 1 , 2]
cb1 = plt.colorbar(hdl, cax=cbar_ax, orientation='horizontal', ticks=clevs)

def get_ax_loc(cbar_ax, render):
    from matplotlib.transforms import Bbox
    tr = ax.transData + fig.transFigure.inverted()
    bbox = Bbox(tr.transform([[1, -0.5], [1.8, 0]]))
    return bbox

def set_children_clip_box(artist, box):
    for c in artist.get_children():
        c.set_clip_box(box)
        set_children_clip_box(c, box)

cbar_ax.set_axes_locator(get_ax_loc)
set_children_clip_box(cbar_ax, hdl.get_clip_box())

plt.show()

And here is the output:

enter image description here

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top