matplotlibbounding-box

matplotlib tick label anchor -- right align tick labels (on right side axis) and "clip" the left (west) side of the tick labels to the axis


I would like to use the "west" anchor for my tick labels for a twinx (right-side) axis. Looking at the plot below, for example, I would like the left side of the tick labels to be aligned with the right axis.

I attempted a few things below, to no avail.

import matplotlib.pyplot as plt
X = [1,2,3]
fig, ax = plt.subplots()
ax.plot(X)
ax.set_ylim([1,3])
ax.set_yticks(X)
axR = ax.twinx()
axR.set_ylim(ax.get_ylim())
axR.set_yticks(ax.get_yticks())
axR.set_yticklabels(['-1.00', '2.00', '3.00'], ha='right')
# axR.set_yticklabels(['-1.00', '2.00', '3.00'], ha='right', bbox_to_anchor='W')
# axR.set_yticklabels(['-1.00', '2.00', '3.00'], ha='right', bbox=dict(bbox_to_anchor='W'))
# bbox can have args from: https://matplotlib.org/stable/api/_as_gen/matplotlib.patches.FancyBboxPatch.html#matplotlib.patches.FancyBboxPatch
fig.show()

enter image description here


Solution

  • So I had the same problem and stumbled on this question. I tried quite a bit and I basically decided I would need to find the right side of the labels when they are left aligned on the right side and then right align them from this point.

    I tried a few things but don't have a lot of experience, so it's not perfect, but it seems to work by finding the coordinates as a bbox. I converted that back and forth to get it as an array (probably a shorter way that I don't know). I then took the gap of the largest one and added that to the spacing.

    A few notes: I'm doing this on a subplot, hence ax2. I've also already moved the axis tick labels to the right side with ax2.yaxis.tick_right()

    r = plt.gcf().canvas.get_renderer()
    coord = ax2.yaxis.get_tightbbox(r)
    
    ytickcoord = [yticks.get_window_extent() for yticks in ax2.get_yticklabels()]
    
    inv = ax2.transData.inverted()
    
    ytickdata = [inv.transform(a) for a in ytickcoord]
    ytickdatadisplay = [ax2.transData.transform(a) for a in ytickdata]
                    
    gap = [a[1][0]-a[0][0] for a in ytickdatadisplay]
    
    for tick in ax2.yaxis.get_majorticklabels():
        tick.set_horizontalalignment("right")
    
    ax2.yaxis.set_tick_params(pad=max(gap)+1)}
    

    Update: I have recently been sent the solution to a similar problem with left alignment on the left side. From this solution, I believe this can be simplified to:

    import matplotlib as mpl
    import matplotlib.pyplot as plt
    
    fig = plt.figure(figsize =(5,3))
    ax = fig.add_axes([0,0,1,1])
    plt.plot([0,100,200])
    ax.yaxis.tick_right()
    
    # Draw plot to have current tick label positions
    plt.draw()
    # Read max width of tick labels
    ytickcoord = max([yticks.get_window_extent(renderer = plt.gcf().canvas.get_renderer()).width for yticks in ax.get_yticklabels()])
    # Change ticks to right aligned
    ax.axes.set_yticklabels(ax.yaxis.get_majorticklabels(),ha = "right")
    # Add max width of tick labels
    ax.yaxis.set_tick_params(pad=ytickcoord+1)
    
    plt.show()
    plt.close("all")