setting axis label offset from end of spine

Ah. I wasn't quite clear on what you were trying to do before.

There's actually a bit of "magic" in there that automatically adjusts the xlabel so it is lower than the xtick values. (If you rotate the xtick values, you can see this in action). So it actually does position things how you want, but then when it goes to draw it is automatically lowering the text a little to make room.

Since I don't see an easy way around this magic, you could not use xlabel at all and just add some Axes text:

    from matplotlib import text
    text = text.Text(1, 0, "Foo")
    text.set_transform(offset_copy(axes.transAxes, axes.figure, x=5, y=0, units='points'))
    text.set_clip_on(False)
    axes.add_artist(text)

Completely non-obvious of course :slight_smile: If there's enough need for the kind of xlabels you're making, we should probably provide a way to do this directly, but it would have to be well-tested to ensure it didn't break existing functionality.

Mike

···

jason-sage@...2130... wrote:

Michael Droettboom wrote:

The xlabel doesn't use the data transform, it uses the axes transform, where the edges of the axes always go from 0.0 to 1.0, regardless of the data extents. Therefore, tou can start with the label's default transform:

trans = xlabel.get_transform()

and use that as the basis for the offset transform

offtrans = offset_copy(trans, subplot.figure, x=5, y=0, units='points')

and set the label's position, alignment and transform:

xlabel.set_position((1, 0))
xlabel.set_ha('right')
xlabel.set_transform(offtrans)

Hope that does what you're after.

Thanks! This helps a lot.
I notice with the following, the x-axis label is lined up with its baseline equal to the baseline of the tick labels. I'd like the baseline to be the same as the actual axis line. What should I do to get the baseline of the x-axis label to be the actual axis line? I've been working on this for a while tonight, and I've made a lot of progress, but now I'm getting confused. Is there some sort of padding going on, even though I set labelpad=0? Is the problem I'm seeing because I started from xlabel.get_transform(), which already has the padding builtin?

           from matplotlib.transforms import offset_copy
           subplot.xaxis.labelpad=0
           xlabel=subplot.xaxis.get_label()
           trans=xlabel.get_transform()
           xlabel.set_horizontalalignment('left')
           xlabel.set_verticalalignment('baseline')
           xlabel.set_transform(offset_copy(trans,subplot.figure,x=5,y=0,units='points'))

           xlabel.set_position((1,0))

Thanks,

Jason

--
Jason Grout

--
Michael Droettboom
Science Software Branch
Operations and Engineering Division
Space Telescope Science Institute
Operated by AURA for NASA

Michael Droettboom wrote:

Ah. I wasn't quite clear on what you were trying to do before.

There's actually a bit of "magic" in there that automatically adjusts the xlabel so it is lower than the xtick values. (If you rotate the xtick values, you can see this in action). So it actually does position things how you want, but then when it goes to draw it is automatically lowering the text a little to make room.

Since I don't see an easy way around this magic, you could not use xlabel at all and just add some Axes text:

   from matplotlib import text
   text = text.Text(1, 0, "Foo")
   text.set_transform(offset_copy(axes.transAxes, axes.figure, x=5, y=0, units='points'))
   text.set_clip_on(False)
   axes.add_artist(text)

Completely non-obvious of course :slight_smile: If there's enough need for the kind of xlabels you're making, we should probably provide a way to do this directly, but it would have to be well-tested to ensure it didn't break existing functionality.

Ah---I realized that there was some automagic positioning going on if I didn't use subplot.xaxis.set_label_coords (thank goodness that I can read the source!). So now I have something that *almost* works perfectly. Here is a complete example:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.transforms import offset_copy
fig = plt.figure()
x = np.linspace(0,2*np.pi,100)
y = 2*np.sin(x)
ax = fig.add_subplot(1,1,1)
ax.plot(x,y)

# works
xaxis='bottom'
not_xaxis='top'

# doesn't work
#xaxis='top'
#not_xaxis='bottom'

# works
yaxis='left'
not_yaxis='right'

# doesn't work
#yaxis='right'
#not_yaxis='left'

ax.spines[xaxis].set_position(('outward',10))
ax.xaxis.set_ticks_position(xaxis)
ax.spines[yaxis].set_position(('outward',10))
ax.yaxis.set_ticks_position(yaxis)

ax.spines[not_xaxis].set_color('none')
ax.spines[not_yaxis].set_color('none')

xlabel=ax.xaxis.get_label()
xlabel.set_horizontalalignment('left')
xlabel.set_verticalalignment('baseline')
trans=ax.spines[xaxis].get_transform()
labeltrans=offset_copy(trans, fig, x=8, y=0, units='points')
ax.xaxis.set_label_coords(x=1,y=0,transform=labeltrans)

ylabel=ax.yaxis.get_label()
ylabel.set_horizontalalignment('center')
ylabel.set_verticalalignment('center')
ylabel.set_rotation('horizontal')
trans=ax.spines[yaxis].get_transform()
labeltrans=offset_copy(trans, fig, x=0, y=12, units='points')
ax.yaxis.set_label_coords(x=0,y=1,transform=labeltrans)

ax.set_xlabel('x axis')
ax.set_ylabel('y axis')

fig.savefig('test.png',bbox_inches='tight')

When I make the x-axis the top spine, or the y-axis the right spine, though (uncomment the "doesn't work" parts above), the labels end up on the wrong side of the axes. Any idea about what is going on here? Is it related to the post I just made to matplotlib-devel entitled "spines with 'axes' positions show in wrong place?"?

Also, I was wondering if I should use the ax.spines[yaxis].get_spine_transform() instead of ax.spines[yaxis].get_transform(). It didn't seem to help in the above issue, but I wasn't sure what the difference was between get_spine_transform and get_transform was.

Thanks,

Jason

···

--
Jason Grout