Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

get aspect ratio of axes

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.)

like image 475
askewchan Avatar asked Jan 11 '17 17:01

askewchan


1 Answers

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)))
like image 97
Mad Physicist Avatar answered Oct 02 '22 18:10

Mad Physicist