在新版本的 matplotlib 中使用 twinx 时定义纵横比

Define aspect ratio when using twinx in new version of matplotlib

当前版本的matplotlib不再允许box-forced,我应该如何做与answer相同的事情?

我正在使用 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 轴,上述代码不起作用,但也不会引发任何错误。
然后我找到了一个answer here

代码基本上使用相同的方法将宽高比设置为 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()

我的 python 中的这段代码不起作用,引发

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 是从什么时候删除的。 那么我们应该如何设置宽高比'box'呢?

谢谢!

供参考:matplotlib.axes.Axes.set_adjustable

正如我 just commented 在各自的 matplotlib 问题上,

"aspect" in matplotlib always refers to the data, not the axes box. Therefore setting the aspect for twinned or shared axes and letting the box be adjustable actually only makes sense when the scales are the same - or differ by an offset (as opposed to any other linear or nonlinear function). Matplotlib does not perform any check on this, so it disallows for adjustable='box' in such case.

在我看来,在这里使用 aspect 只是为轴框获取固定比率的一种解决方法。截至目前,Matplotlib 并未为此提供任何明确的代码路径,但可以例如通过调整子图参数

将轴框强制为正方形 space
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()

另请参阅 this answer 了解多个子图。

如果你想使用 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()

缺点是必须决定使用哪种尺寸(以英寸为单位)。 虽然我不完全理解我的代码...