Is there an easy and reliable way to determine the current aspect ratio of an axes when its aspect is set to \'auto\'?
The obvious thing to
How about
import numpy as np
aspect = sum(np.abs(ax.get_xlim())) / sum(np.abs(ax.get_ylim()))
The best thing I can find is this:
def get_aspect(ax=None):
if ax is None:
ax = plt.gca()
fig = ax.figure
ll, ur = ax.get_position() * fig.get_size_inches()
width, height = ur - ll
axes_ratio = height / width
aspect = axes_ratio / ax.get_data_ratio()
return aspect
But it's surprisingly complicated, and I'm not sure if it is reliable under transforms, etc, as I know nothing about the bbox and transform objects.
I am not sure if it does exactly what you want, but try it
bbox = axis.get_window_extent().transformed(figure.dpi_scale_trans.inverted())
aspect_ratio = bbox.width / bbox.height
From the docs, the aspect ratio is the ratio of data-to-display scaling units in the x- and y-axes. i.e., if there are 10 data units per display in the y-direction and 1 data unit per display unit in the x-direction, the ratio would be 1/10. The circle would be 10 times wider than it is tall. This corresponds to the statement that an aspect of num does the following:
a circle will be stretched such that the height is num times the width. aspect=1 is the same as aspect=’equal’.
Based on the same original piece of code that you looked at (matplotlib.axes._base.adjust_aspect, starting around line 1405), I think we can come up with a simplified formula as long as you only need this ratio for linear Cartesian axes. Things get complicated with polar and logarithmic axes, so I will ignore them.
To reiterate the formula:
(x_data_unit / x_display_unit) / (y_data_unit / y_display_unit)
This happens to be the same as
(y_display_unit / x_display_unit) / (y_data_unit / x_data_unit)
This last formulation is just the ratio of the display sizes in the two directions divided by the ratio of the x and y limits. Note that ax.get_data_ratio does NOT apply here because that returns the results for the actual data bounds, not the axis limits at all:
from operator import sub
def get_aspect(ax):
# Total figure size
figW, figH = ax.get_figure().get_size_inches()
# Axis size on figure
_, _, w, h = ax.get_position().bounds
# Ratio of display units
disp_ratio = (figH * h) / (figW * w)
# Ratio of data units
# Negative over negative because of the order of subtraction
data_ratio = sub(*ax.get_ylim()) / sub(*ax.get_xlim())
return disp_ratio / data_ratio
Now let's test it:
from matplotlib import pyplot as plt
fig, ax = plt.subplots()
ax.set_aspect('equal')
print('{} == {}'.format(ax.get_aspect(), get_aspect(ax)))
ax.set_aspect(10)
print('{} == {}'.format(ax.get_aspect(), get_aspect(ax)))