Получить соотношение сторон осей

Существует ли простой и надежный способ определения текущего соотношения сторон axes когда его аспект установлен в 'auto'?

Очевидная вещь, чтобы проверить это ax.get_aspect(), но это только возвращает 'auto', Я могу установить произвольное постоянное значение ax.set_aspect(aspect)после чего эта же константа возвращается ax.get_aspect(), По умолчанию (и очень полезно) у нас есть aspect = 'auto', в этом случае соотношение сторон автоматически рассчитывается и корректируется в соответствии с пределами данных и размером осей.
Как я могу получить числовое соотношение сторон, которое было выбрано автоматически?

Для пояснения, это не соотношение сторон пределов данных, возвращаемых ax.get_data_ratio()ни соотношение сторон для размера отображения рисунка или субплота, возвращенного fig.get_figheight() / fig.get_figwidth() (для рисунка). Это немного неуловимо, так как зависит как от размера дисплея, так и от пределов данных. (Это может привести к путанице между различными соотношениями и причиной, по которой я считаю важным сделать ее легко доступной.)

3 ответа

Из документов соотношение сторон является отношением единиц масштабирования данных к отображению по осям X и Y. т. е. если имеется 10 единиц данных на дисплей в направлении у и 1 единица данных на дисплей в направлении х, соотношение будет 1/10. Круг будет в 10 раз шире, чем высокий. Это соответствует утверждению, что аспект num делает следующее:

круг будет вытянут так, чтобы высота была в num раз больше ширины. аспект =1 - это то же самое, что аспект = "равно".

Основан на том же оригинальном фрагменте кода, на который вы смотрели (matplotlib.axes._base.adjust_aspect , начиная с линии 1405), я думаю, что мы можем придумать упрощенную формулу, если вам нужно только это соотношение для линейных декартовых осей. Все усложняется с полярными и логарифмическими осями, поэтому я буду игнорировать их.

Чтобы повторить формулу:

(x_data_unit / x_display_unit) / (y_data_unit / y_display_unit)

Это происходит так же, как

(y_display_unit / x_display_unit) / (y_data_unit / x_data_unit)

Эта последняя формулировка представляет собой просто отношение размеров дисплея в двух направлениях, деленное на отношение пределов x и y. Обратите внимание, что ax.get_data_ratio здесь НЕ применяется, потому что возвращает результаты для фактических границ данных, а не для осей вообще:

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

Теперь давайте проверим это:

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

Лучшее, что я могу найти, это:

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

Но это удивительно сложно, и я не уверен, что он надежен при преобразованиях и т. Д., Поскольку я ничего не знаю о bbox а также transform объекты.

Как насчет

import numpy as np
aspect = sum(np.abs(ax.get_xlim())) / sum(np.abs(ax.get_ylim()))

Я не уверен, что он делает именно то, что вы хотите, но попробуйте

    bbox = axis.get_window_extent().transformed(figure.dpi_scale_trans.inverted())
    aspect_ratio = bbox.width / bbox.height
Другие вопросы по тегам