pythonmatplotlib

Pyplot surface plot z axis scaling label


When z-values become small the z-axis gives wrong magnitude.

Example:

import numpy as np

import matplotlib.pyplot as plt

import matplotlib

print('matplotlib version : ', matplotlib.__version__)

X = np.arange(-5, 5, 0.25)
Y = np.arange(-5, 5, 0.25)
X, Y = np.meshgrid(X, Y)
R = np.sqrt(X**2 + Y**2)
Z = np.sin(R)
r = 0.0001 # scale z values 

fig, ax = plt.subplots(subplot_kw={"projection": "3d"})

# Plot the surface.
surf = ax.plot_surface(X, Y, r*Z, cmap=matplotlib.cm.coolwarm,
                       linewidth=0, antialiased=False)
                       
plt.show()

when taking e.g. r=0.0001 the z-axis should be labelled with 1e-5 or something like that to be correct, but it uses the same exact z-ticks as plotting with r=1.

Edit: added plot. The ticks on the z-axis should be adjusted but aren't. (Just want a an overall 1e-5, for the axis not on each tick)

enter image description here

Expected image for ir = 0.000000000000000000000001 # scale z values ????

enter image description here


Solution

  • This is caused by a bug that is present in Matplotlib v3.7 (all minor versions). It was fixed in Matplotlib release 3.8. So, I recommend upgrading your Matplotlib version, e.g.,

    pip install matplotlib==3.8
    

    The problem also does not seem to occur in releases prior to 3.7, so you could also downgrade to version 3.6.3, e.g.,

    pip install matplotlib==3.6.3
    

    if upgrading is not possible.

    If upgrading or downgrading are not possible, then you can manually apply the fix from the pull request, i.e.,

    import numpy as np
    import matplotlib.pyplot as plt
    import matplotlib
    
    X = np.arange(-5, 5, 0.25)
    Y = np.arange(-5, 5, 0.25)
    X, Y = np.meshgrid(X, Y)
    R = np.sqrt(X**2 + Y**2)
    Z = np.sin(R)
    r = 0.0001 # scale z values 
    
    fig, ax = plt.subplots(subplot_kw={"projection": "3d"})
    
    # Plot the surface.
    surf = ax.plot_surface(X, Y, r*Z, cmap=matplotlib.cm.coolwarm,
                           linewidth=0, antialiased=False)
    
    # fix the z-axis offset positioning 
    ax.zaxis.offsetText._transform = ax.zaxis.axes.transData
    
    plt.show()
    

    The above answer does not fix plots rendered in a Juptyer notebook as standard (i.e., with or without %matplotlib inline magic), i.e., even with Matplotlib v3.8, the offset exponent value on the axis will not show up. Instead, to make things work as expected, you must use interactive plotting (via ipympl) by starting your notebook cell with %matplotlib widget or %matplotlib ipympl.