8

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()
yngwaz
  • 769
  • 1
  • 9
  • 17
  • 1
    The colorbar goes in it's own axis object so you _can't_ specify it's location in data units. You can sort out what the correct figure coordinates would be to be at a given place in data-space, but it will not update if you pan/zoom. – tacaswell Apr 04 '14 at 12:35
  • See: http://matplotlib.org/1.3.1/users/transforms_tutorial.html You want to do data -> screen -> figure – tacaswell Apr 04 '14 at 12:37
  • Thank's a lot, your comment helped to solve my question. – yngwaz Apr 04 '14 at 13:16
  • Could you write up what you did as an answer to your own question? – tacaswell Apr 04 '14 at 13:31
  • I wanted to, but my lack of reputation prevents me of answering my own question within 8hours or so... That's why I edited my question with the answer... – yngwaz Apr 04 '14 at 13:36
  • interesting...well, please do it when it will let you, both for house keeping and so you can get rep if others find it useful. – tacaswell Apr 04 '14 at 13:48

2 Answers2

2

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()
yngwaz
  • 769
  • 1
  • 9
  • 17
1

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

HYRY
  • 94,853
  • 25
  • 187
  • 187
  • Thank's for this solution, it looks more robust than mine. The only drawback is, that I am not able to pass the data coordinates to the function get_ax_loc. – yngwaz Apr 07 '14 at 07:46