【问题标题】:Share scaling of differntly sized subplots' axes (not sharing axes)共享不同大小的子图轴的缩放(不共享轴)
【发布时间】:2019-05-31 03:08:56
【问题描述】:

使用 matplotlib,我想绘制两个具有相同 x 轴比例的图形,但我想显示不同大小的部分。我怎样才能做到这一点?

到目前为止,我可以使用 GridSpec 绘制不同大小的子图或共享 x 轴的相同大小的子图。当我同时尝试两者时,较小的子图具有相同的轴但比例较小,而我想要相同的比例和不同的轴,因此共享轴可能是一个错误的想法。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

x=np.linspace(0,10,100)
y=np.sin(x)

x2=np.linspace(0,5,60)
y2=np.cos(x2)

fig=plt.figure()

gs=GridSpec(2,3)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x,y)

ax2 = fig.add_subplot(gs[1,:-1])
    #using sharex=ax1 here decreases the scaling of ax2 too much
ax2.plot(x2,y2)

plt.show()    

我希望 x.axes 具有相同的缩放比例,即相同的 x 值总是彼此完全重叠,this 应该会给你一个想法。较小的情节的框架可以扩大或适合情节,没关系。 As it is now,比例不匹配。

提前致谢。

【问题讨论】:

  • 带有绘图问题,如果您可以提供您想要的可视化表示,这将非常有帮助。我怀疑你会想使用 get_ 函数从 ax1 中提取轴信息,然后在纠正大小差异后根据需要使用 ax2.set_
  • 我添加了一个图表,说明如果看起来是什么样子,希望您能明白我的意思。

标签: python matplotlib scaling subplot


【解决方案1】:

这还是有点粗糙。我确信有一种更优雅的方法可以做到这一点,但是您可以在ax2 的轴坐标和ax1 的数据坐标之间创建一个自定义transformation(请参阅Transformations Tutorial)。也就是说,你计算ax2左右边缘对应位置的data-value(根据ax1),然后相应调整ax2xlim

这是一个演示,即使第二个子图与第一个子图没有以任何特定方式对齐,它也能正常工作。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec

x=np.linspace(0,25,100)
y=np.sin(x)

x2=np.linspace(10,30,60)
y2=np.cos(x2)

fig=plt.figure()

gs=GridSpec(2,6)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x,y)

ax2 = fig.add_subplot(gs[1,3:-1])
ax2.plot(x2,y2)

# here is where the magic happens
trans = ax2.transAxes + ax1.transData.inverted()
((xmin,_),(xmax,_)) = trans.transform([[0,1],[1,1]])
ax2.set_xlim(xmin,xmax)

# for demonstration, show that the vertical lines end up aligned
for ax in [ax1,ax2]:
    for pos in [15,20]:
        ax.axvline(pos)

plt.show()

编辑:一种可能的改进是在the xlim_changed event callback 中进行转换。这样一来,即使在第一个轴上进行缩放/平移时,轴也会保持同步。

正如您所说,tight_layout() 也存在一个小问题,但可以通过直接调用回调函数轻松解决。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec


def on_xlim_changed(event):
    # here is where the magic happens
    trans = ax2.transAxes + ax1.transData.inverted()
    ((xmin, _), (xmax, _)) = trans.transform([[0, 1], [1, 1]])
    ax2.set_xlim(xmin, xmax)


x = np.linspace(0, 25, 100)
y = np.sin(x)

x2 = np.linspace(10, 30, 60)
y2 = np.cos(x2)

fig = plt.figure()


gs = GridSpec(2, 6)

ax1 = fig.add_subplot(gs[0, :])
ax1.plot(x, y)

ax2 = fig.add_subplot(gs[1, 3:-1])
ax2.plot(x2, y2)

# for demonstration, show that the vertical lines end up aligned
for ax in [ax1, ax2]:
    for pos in [15, 20]:
        ax.axvline(pos)

# tight_layout() messes up the axes xlim
# but can be fixed by calling on_xlim_changed()
fig.tight_layout()
on_xlim_changed(None)

ax1.callbacks.connect('xlim_changed', on_xlim_changed)


plt.show()

【讨论】:

  • 谢谢,这正是我所需要的,无论子图位于何处,它都能完美运行。我注意到有助于防止任何错误(但可能非常明显)的两件事是在转换之前设置网格、刻度等。也不适用于紧凑的布局
  • 我添加了一些改进,以在平移/缩放时保持轴同步,并修复了tight_layout()
【解决方案2】:

我建议根据ax1的限制设置第二个轴的限制。

试试这个!

ax2 = fig.add_subplot(gs[1,:-1])
ax2.plot(x2,y2)
lb, ub = ax1.get_xlim()
# Default margin is 0.05, which would be used for auto-scaling, hence reduce that here
# Set lower bound and upper bound based on the grid size, which you choose for second plot
ax2.set_xlim(lb, ub *(2/3) -0.5)

plt.show()   

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-22
    • 2020-07-07
    • 1970-01-01
    相关资源
    最近更新 更多