创建一个颜色条可以相对于彼此移动两个先前对齐的轴对象 - Matplotlib

时间:2017-10-21 12:29:33

标签: python matplotlib plot colorbar

我的“真实世界”问题我想重新计算在我的数字的刻度标签中写入的xy值之后我放大了它的原点总是在(0,0)并且显然是x轴和y轴上的值的相对距离保持不变。 我的问题在这个帖子中解决了: Initial solution

该解决方案包括创建一个隐藏绘图的不可见轴和一个在缩放后获得不同刻度标记的可见轴。

在我的情况下,我想叠加多个countor和coutourf图。对于这些图中只有一个我想在图中添加一个颜色条! 但是当我在我的脚本中创建颜色条时,我创建的两个轴对象相对于彼此移位。如果没有颜色条,它们就完全对齐了!

这是一个大致重现行为的MWE:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import mlab, cm

# Default delta is large because that makes it fast, and it illustrates
# the correct registration between image and contours.
delta = 0.5

extent = (-3, 4, -4, 3)

x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10

levels = np.arange(-2.0, 1.601, 0.4)  # Boost the upper limit to avoid truncation errors.

norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn




# ax is empty
fig, ax = plt.subplots()
ax.set_navigate(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=2)

ax2.contourf(X, Y, Z, levels,
                 cmap=cm.get_cmap(cmap, len(levels) - 1),
                 norm=norm,
                 )
ax2.axis("off")

ax.set_xlim(ax2.get_xlim())
ax.set_ylim(ax2.get_ylim())

#
# Declare and register callbacks
def on_lims_change(axes):
    # change limits of ax, when ax2 limits are changed.
    a=ax2.get_xlim()
    ax.set_xlim(0, a[1]-a[0])
    a=ax2.get_ylim()
    ax.set_ylim(0, a[1]-a[0])



sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm ) #Do not show unnecessary parts of the colormap
sm._A = []
cb = plt.colorbar(sm,extend="both", label="units")
cb.ax.tick_params(labelsize=10)


ax2.callbacks.connect('xlim_changed', on_lims_change)
ax2.callbacks.connect('ylim_changed', on_lims_change)
ax.axis('scaled')
plt.axis('scaled')
# Show
plt.show()

现在,轮廓图似乎被转移到可见轴上。我在网上发现了一些提示,即“彩条盒自动从它所连接的轴上占据空间” Link1 Link2

但我真的不知道我需要做些什么来改变这种行为,我也不明白我的问题是否相关。

请注意,该部分:

ax.axis('scaled')
plt.axis('scaled')

是必要的,因为我需要保持宽高比与数据集中的完全一样!

提前谢谢!

1 个答案:

答案 0 :(得分:1)

通过

添加颜色条后,您可以将ax(带有标签的空轴)的位置更改为ax2(显示数据的轴)的位置
ax.set_position(ax2.get_position())

或者,通过从两个轴“钢化”空间来创建颜色条

cb = fig.colorbar(sm,ax=[ax,ax2], extend="both", label="units")

这两个解决方案都在this linked question的答案中找到。


以下是除问题实际范围之外的一些其他改进:

ax.axis('scaled')
ax2.axis('scaled') 

此外,如果ax,请将ax2置于顶部,以使contourf图不与轴刺重叠。

# put `ax` on top, to let the contours not overlap the shown axes
ax.set_zorder(2)     
ax.patch.set_visible(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=1)

完整代码:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import mlab, cm

delta = 0.5
extent = (-3, 4, -4, 3)
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = mlab.bivariate_normal(X, Y, 1.0, 1.0, 0.0, 0.0)
Z2 = mlab.bivariate_normal(X, Y, 1.5, 0.5, 1, 1)
Z = (Z1 - Z2) * 10

levels = np.arange(-2.0, 1.601, 0.4) 

norm = cm.colors.Normalize(vmax=abs(Z).max(), vmin=-abs(Z).max())
cmap = cm.PRGn

# ax is empty
fig, ax = plt.subplots()
ax.set_navigate(False)
 # put `ax` on top, to let the contours not overlap the shown axes
ax.set_zorder(2)     
ax.patch.set_visible(False)
# ax2 will hold the plot, but has invisible labels
ax2 = fig.add_subplot(111,zorder=1)

ax2.contourf(X, Y, Z, levels,
                 cmap=cm.get_cmap(cmap, len(levels) - 1),
                 norm=norm,
                 )
ax2.axis("off")

ax.set_xlim(ax2.get_xlim())
ax.set_ylim(ax2.get_ylim())

#
# Declare and register callbacks
def on_lims_change(axes):
    # change limits of ax, when ax2 limits are changed.
    a=ax2.get_xlim()
    ax.set_xlim(0, a[1]-a[0])
    a=ax2.get_ylim()
    ax.set_ylim(0, a[1]-a[0])


sm = plt.cm.ScalarMappable(cmap=cmap, norm=norm )
sm._A = []
cb = fig.colorbar(sm,ax=[ax,ax2], extend="both", label="units")
cb.ax.tick_params(labelsize=10)

ax2.callbacks.connect('xlim_changed', on_lims_change)
ax2.callbacks.connect('ylim_changed', on_lims_change)
ax.axis('scaled')
ax2.axis('scaled')
#ax.set_position(ax2.get_position())
# Show
plt.show()

enter image description here