Skip to content

Instantly share code, notes, and snippets.

@thomasaarholt
Last active January 23, 2023 15:52
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save thomasaarholt/c8440b132aaea9f71f0588af486ad457 to your computer and use it in GitHub Desktop.
Save thomasaarholt/c8440b132aaea9f71f0588af486ad457 to your computer and use it in GitHub Desktop.
Matplotlib autoscale
def autoscale(ax=None, axis='y', margin=0.1):
'''Autoscales the x or y axis of a given matplotlib ax object
to fit the margins set by manually limits of the other axis,
with margins in fraction of the width of the plot
Defaults to current axes object if not specified.
'''
import matplotlib.pyplot as plt
import numpy as np
if ax is None:
ax = plt.gca()
newlow, newhigh = np.inf, -np.inf
for artist in ax.collections + ax.lines:
x,y = get_xy(artist)
if axis == 'y':
setlim = ax.set_ylim
lim = ax.get_xlim()
fixed, dependent = x, y
else:
setlim = ax.set_xlim
lim = ax.get_ylim()
fixed, dependent = y, x
low, high = calculate_new_limit(fixed, dependent, lim)
newlow = low if low < newlow else newlow
newhigh = high if high > newhigh else newhigh
margin = margin*(newhigh - newlow)
setlim(newlow-margin, newhigh+margin)
def calculate_new_limit(fixed, dependent, limit):
'''Calculates the min/max of the dependent axis given
a fixed axis with limits
'''
if len(fixed) > 2:
mask = (fixed>limit[0]) & (fixed < limit[1])
window = dependent[mask]
low, high = window.min(), window.max()
else:
low = dependent[0]
high = dependent[-1]
if low == 0.0 and high == 1.0:
# This is a axhline in the autoscale direction
low = np.inf
high = -np.inf
return low, high
def get_xy(artist):
'''Gets the xy coordinates of a given artist
'''
if "Collection" in str(artist):
x, y = artist.get_offsets().T
elif "Line" in str(artist):
x, y = artist.get_xdata(), artist.get_ydata()
else:
raise ValueError("This type of object isn't implemented yet")
return x, y
# To test
fig, axes = plt.subplots(ncols = 4, figsize=(12,3))
(ax1, ax2, ax3, ax4) = axes
x = np.linspace(0,100,300)
noise = np.random.normal(scale=0.1, size=x.shape)
y = 2*x + 3 + noise
for ax in axes:
ax.plot(x, y)
ax.scatter(x,y, color='red')
ax.axhline(50., ls='--', color='green')
for ax in axes[1:]:
ax.set_xlim(20,21)
ax.set_ylim(40,45)
autoscale(ax3, 'y', margin=0.1)
autoscale(ax4, 'x', margin=0.1)
ax1.set_title('Raw data')
ax2.set_title('Specificed limits')
ax3.set_title('Autoscale y')
ax4.set_title('Autoscale x')
plt.tight_layout()
fig.savefig('autoscale.png', dpi=400)
@smjure
Copy link

smjure commented Sep 2, 2020

Thanks for the code, have found it on the Stackoverflow, works great on the sample example. I've tried to expand it to subplots, i.e. when one zooms a subplot with mouse, all other y axes auto-adjust or auto-scale as well, something like this:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,100,1001)
y = np.sin(x/16) + np.cumsum(np.random.randn(1001))/30.

nb_subs = 2
fig, axs = plt.subplots(nb_subs,1,sharex=True)
for ax in axs:
    ax.plot(x,y)
    autoscale(ax, 'y', margin=0.1)
    
plt.show()

But have not managed to make it work, I realized my programming knowledge pretty limited. Have also searched a lot on web, also explored links that point to/from the Stack post. Would appreciate some help here to expand it to all subplots, thank you!

@generaldamras
Copy link

@smjure that's the same problem I've been trying to face.

It looks like there's no immediate solution. The problem is that autoscale will run once, at the beginning, but not every time someone zooms into the plots. You can repeatedly call autoscale whenever you need it, on all of your axes, but I'm sure that's not a desirable solution.

The other option is to add event-based callbacks to the figure. So if you click in the figure window, you run the code. You'd need to use something like this: https://matplotlib.org/3.2.1/api/_as_gen/matplotlib.pyplot.connect.html. For example, https://stackoverflow.com/a/33641336/6828087.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment