Определите соотношение сторон при использовании 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()

Недостатком является то, что нужно решить, какой размер использовать в дюймах. Я не полностью понимаю мой код, хотя...

Другие вопросы по тегам