使用twinx时如何定义长宽比

5 投票
2 回答
3267 浏览
提问于 2025-04-18 13:14

我该如何为使用 twinx 的图表设置宽高比呢?


下面,我提供了三个例子:

  1. 没有使用 twinx,来强调我是如何设置宽高比的
  2. 仅使用 twinx,展示之前设置的宽高比是如何被重置的
  3. 我尝试在使用 twinx 时为两个 y 轴定义宽高比(但这并不奏效)

我使用的是 matplotlib 1.3.0
以下脚本展示了我如何设置一个简单图表的宽高比:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,1.6,50) + 50.0

fig = plt.figure()

ax = fig.add_subplot(111)

XLIM = [50.0, 51.6]
YLIM = [0.0, 1.1, 0.0, 11.0] 

ax.plot(x,np.sin(x-50.0),'b')

ax.set_xlim([XLIM[0], XLIM[1]])
ax.set_ylim([YLIM[0], YLIM[1]])

ax.set_xticks(np.arange(XLIM[0], XLIM[1], 0.2))
ax.set_yticks(np.arange(YLIM[0], YLIM[1]+0.1, 0.1)[:-1])

ax.grid(True,which='major',linestyle='solid')

ax.set_aspect((XLIM[1]-XLIM[0])/(YLIM[1]-YLIM[0]))

plt.show()

这个图的宽高比是固定的:

first plot

但是,如果我想使用 twinx() 函数添加一个额外的 y 轴,宽高比就会被重置:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,1.6,50) + 50.0

fig = plt.figure()

ax = fig.add_subplot(111)
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')

ax.set_xlim([XLIM[0], XLIM[1]])
ax.set_ylim([YLIM[0], YLIM[1]])

ax.set_xticks(np.arange(XLIM[0], XLIM[1], 0.2))
ax.set_yticks(np.arange(YLIM[0], YLIM[1]+0.1, 0.1)[:-1])

ax.grid(True,which='major',linestyle='solid')

ax.set_aspect((XLIM[1]-XLIM[0])/(YLIM[1]-YLIM[0]))

plt.show()

这导致了这个图(左侧之前定义的 y 范围被重置):

first twinx attempt

现在,再次尝试指定宽高比并没有得到预期的结果:

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0,1.6,50) + 50.0

fig = plt.figure()

ax = fig.add_subplot(111)
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')

ax.set_xlim([XLIM[0], XLIM[1]])
ax.set_ylim([YLIM[0], YLIM[1]])
ax2.set_ylim([YLIM[2], YLIM[3]])

ax.set_xticks(np.arange(XLIM[0], XLIM[1], 0.2))
ax.set_yticks(np.arange(YLIM[0], YLIM[1]+0.1, 0.1)[:-1])
ax2.set_yticks(np.arange(YLIM[2], YLIM[3]+1.0, 1.0))

ax.grid(True,which='major',linestyle='solid')

ax.set_aspect((XLIM[1]-XLIM[0])/(YLIM[1]-YLIM[0]))
ax2.set_aspect((XLIM[1]-XLIM[0])/(YLIM[3]-YLIM[2]))

plt.show()

这产生了:

attempt to fix the aspect ratio

我缺少了什么才能生成像第一个图那样的图,但有两个不同刻度的 y 轴呢?在使用 twinx 时,我该如何定义宽高比?

2 个回答

1

Matplotlib 3.1 版本去掉了 'box-forced' 这个选项。现在,Joe 的解决方案会出现一个错误,提示 ValueError: 'box-forced' 不是 adjustable 的有效值;支持的值有 'box' 和 'datalim'。你可以在这个链接找到一些解决方法:在新版本的 matplotlib 中使用 twinx 时定义宽高比

7

看看坐标轴的 adjustable 参数。这个参数控制当你改变比例或数据范围时,数据的限制或者边界矩形的形状是否会改变。

通常情况下,这个参数的选项有 "box""datalim",但对于共享坐标轴来说,有一个特殊的情况。在你的情况下,你需要设置 adjustable='box-forced'

这里有个简单的例子(我稍微简化了一下内容,如果你觉得使用单独的 set_foo 方法更清晰,也可以这样做。如果你选择这种方式,方法是 ax.set_adjustable。):

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

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

enter image description here

撰写回答