pythonmatplotlib

Moving the Scientific Notation Offset of a Pyplot Graph below the top Spine


I'm stacking multiple graphs using matplotlib-pyplot. This is my minimal reproducable example:

from matplotlib import pyplot as plt
from numpy import linspace, sqrt, pi
fig, ax = plt.subplots(3, 1, sharex=True)
plt.subplots_adjust(hspace=0)


def zr(w0, l):
    return pi*w0**2/l

def w0(l, L, R):
    return sqrt(l/pi * sqrt(L*(R-L)))


def width(z, l):
    return w0(l, 0.55, 1) * sqrt(1 + (z / zr(w0(l, 0.55, 1), l))**2)

x = linspace(0, 1, 5)
y = width(x, 5.54e-6)

for i in range(3):
    #Do some analysis here, then plot the fit

    ax[i].set_xlabel("z / m")
    ax[i].set_ylabel("$d(w)$ / mm")
    ax[i].ticklabel_format(axis='y', style='sci', scilimits=(0, 0)) 
    ax[i].grid("both", linestyle=":")

    #The Fix i tried:
    ax[i].get_yaxis().get_offset_text().set_position((0,0.7))

ax[1].errorbar(x, y, xerr = 0.025, yerr = 0.2*1e-3, color = "black", linestyle = "")

plt.show()

Unfortunately in my example, the offset modifier intersects with the data in the graph above.
To fix that I want to move it below the spine. I originally tried this answer, as can be seen above, but I was unable to observe any change.

three stacked graphs made in matplotlib

EDIT: I did a little bit of experimenting. Before the canvas is drawn the position is set correctly, but the offset text is empty:

>>> ax[2].get_yaxis().get_offset_text()
Text(0, 0.7, '')

But after the canvas is drawn the position changes:

>>> fig.canvas.draw()
>>> ax[1].get_yaxis().get_offset_text()
Text(0, 303.3666666666667, '1e−3')

This also holds for plt.plot() which almost certainly calls canvas.draw()


Solution

  • Based on the answer suggested by jared I was abled to find a workaround.

    fig.canvas.draw() #This forces the text to be set
    
    #Replace offset text
    for i in range(3):
        offset_text = ax[i].get_yaxis().get_offset_text()
        ax[i].annotate(offset_text.get_text(), xy = (.01, .85), xycoords='axes fraction')
        offset_text.set_visible(False)
    
    

    This forces the canvas to render, takes the offset text and replaces it with an annotation.