Получить соотношение сторон осей
Существует ли простой и надежный способ определения текущего соотношения сторон 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