Определите соотношение сторон при использовании twinx в новой версии matplotlib
Текущая версия matplotlib не позволяет box-forced
больше, как я должен делать то же самое, что и ответ?
Я использую matplotlib 3.1.0. После того, как я нанес другой набор данных на том же графике с twinx()
функция, я хочу изменить соотношение сторон фактической площади сюжета до 1.
Обычно я делаю это, и это работает для оси не-twinx
ratio = 1
xleft, xright = ax.get_xlim()
ybottom, ytop = ax.get_ylim()
ax.set_aspect(abs((xright - xleft) / (ybottom - ytop)) * ratio)
Для оси twinx приведенный выше код не работает, но также не вызывает никаких ошибок.
Тогда я нашел ответ здесь
Код в основном использовал тот же метод, чтобы установить соотношение сторон 1, только с box-forced
вариант.
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 1.6, 50) + 50.0
fig, ax = plt.subplots()
ax2 = ax.twinx()
XLIM = [50.0, 51.6]
YLIM = [0.0, 1.1, 0.0, 11.0]
ax.plot(x, np.sin(x - 50.0), 'b')
ax2.plot(x, np.cos(x - 50.0) * 10., 'r')
# set aspect to 1
ax.set(adjustable='box-forced',
xlim=XLIM, ylim=YLIM[:2],
xticks=np.arange(XLIM[0], XLIM[1], 0.2),
yticks=np.arange(YLIM[0], YLIM[1] + 0.1, 0.1)[:-1],
aspect=(XLIM[1] - XLIM[0]) / (YLIM[1] - YLIM[0]))
ax2.set(adjustable='box-forced',
ylim=YLIM[2:],
yticks=np.arange(YLIM[2], YLIM[3] + 1.0, 1.0),
aspect=(XLIM[1] - XLIM[0]) / (YLIM[3] - YLIM[2]))
ax.grid(True, which='major', linestyle='solid')
plt.show()
Этот код в моем питоне не работает, поднимает
ValueError: 'box-forced' is not a valid value for adjustable; supported values are 'box', 'datalim'
И если я изменю это на 'box'
, это дает
RuntimeError: Adjustable 'box' is not allowed in a twinned Axes. Use 'datalim' instead.
Я не уверен, когда box-forced
был удален. Теперь, как мы должны установить соотношение сторон в "рамке"?
Спасибо!
Для справки: https://matplotlib.org/3.1.0/api/_as_gen/matplotlib.axes.Axes.set_adjustable.html
2 ответа
Как я только что прокомментировал соответствующий вопрос Matplotlib,
"аспект" в matplotlib всегда относится к данным, а не к блоку осей. Поэтому установка аспекта для сдвоенных или совместно используемых осей и возможность настройки блока на самом деле имеет смысл только тогда, когда шкалы одинаковы - или отличаются смещением (в отличие от любой другой линейной или нелинейной функции). Matplotlib не выполняет никаких проверок, поэтому в этом случае запрещает настраиваемый ='box'.
Мне кажется, что использование аспекта здесь - это просто обходной путь для получения фиксированного соотношения для блока осей. Matplotlib не предоставляет какой-либо четкой кодовой дорожки для этого на данный момент, но можно, например, принудительно заставить блок осей в квадратное пространство, регулируя параметры подплота
import numpy as np
import matplotlib.pyplot as plt
def squarify(fig):
w, h = fig.get_size_inches()
if w > h:
t = fig.subplotpars.top
b = fig.subplotpars.bottom
axs = h*(t-b)
l = (1.-axs/w)/2
fig.subplots_adjust(left=l, right=1-l)
else:
t = fig.subplotpars.right
b = fig.subplotpars.left
axs = w*(t-b)
l = (1.-axs/h)/2
fig.subplots_adjust(bottom=l, top=1-l)
x = np.linspace(0,1.6,50) + 50.0
fig, ax = plt.subplots()
ax2 = ax.twinx()
ax.set(xlim = [50.0, 51.6], ylim = [0.0, 1.1])
ax2.set(ylim = [0.0, 11.0])
ax.plot(x,np.sin(x-50.0),'b')
ax2.plot(x,np.cos(x-50.0)*10.,'r')
ax.grid(True, which='major',linestyle='solid')
squarify(fig)
fig.canvas.mpl_connect("resize_event", lambda evt: squarify(fig))
plt.show()
Также см. Этот ответ для более чем одного подзаговора.
Если вы хотите использовать mpl_toolkits
и испачкайте руки, этот ответ будет хорошим чтением.
Благодаря @ImportanceOfBeingErnest, но, чтобы сделать эту работу в нескольких сюжетах, я нашел другой способ, вдохновленный вашим ответом:
import matplotlib.pyplot as plt
from mpl_toolkits.axes_grid1 import Divider, Size
from mpl_toolkits.axes_grid1.axes_divider import AxesDivider
def make_patch_spines_invisible(ax):
ax.set_frame_on(True)
ax.patch.set_visible(False)
for sp in ax.spines.values():
sp.set_visible(False)
def demo_fixed_size_axes():
fig, axs = plt.subplots(1, 2, figsize=(12, 9))
axs[0].plot([1, 2, 3])
axs[1].plot([1, 2, 3.5])
ax3 = axs[1].twinx()
ax3.plot([1, 2, 3], [1, 25, 30])
axs[1].spines['right'].set_visible(False)
make_patch_spines_invisible(ax4Alt)
ax4Alt.spines['right'].set_visible(True)
for ax in fig.get_axes():
figPos = AxesDivider(ax).get_position()
h = [Size.Fixed(4)] # has to be fixed
v = h
divider = Divider(fig, figPos, h, v, aspect=False)
ax.set_axes_locator(divider.new_locator(nx=0, ny=0))
if __name__ == "__main__":
demo_fixed_size_axes()
plt.show()
Недостатком является то, что нужно решить, какой размер использовать в дюймах. Я не полностью понимаю мой код, хотя...