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 check is ax.get_aspect()
, but that just returns 'auto'
. I can set it to an arbitrary constant value by ax.set_aspect(aspect)
, after which that same constant is returned by ax.get_aspect()
. By default (and very usefully) we have aspect = 'auto'
, in which case the aspect ratio is automatically calculated and adjusted to match the data limits and axes size.
How can I get the numeric aspect ratio that was automatically selected?
To clarify, this is neither the aspect ratio of the data limits returned by ax.get_data_ratio()
, nor the aspect ratio of the display size of the figure or subplot returned by fig.get_figheight() / fig.get_figwidth()
(for the figure). It's a bit subtle, as it depends on both the display size and the data limits. (Which can lead to confusing the different ratios and the reason I find it important to have it easily accessible.)
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)))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With