确保 matplotlib colorbar 在多个图中看起来相同

Ensure matplotlib colorbar looks the same across multiple figure

考虑以下代码

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

def make_Z(X,Y, offset=0.):
    return np.sin(X) + np.cos(Y) + offset

class MidpointNormalize(colors.Normalize):
    def __init__(self, vmin=None, vmax=None, midpoint=None, clip=False):
        self.midpoint = midpoint
        colors.Normalize.__init__(self, vmin, vmax, clip)

    def __call__(self, value, clip=None):
        # I'm ignoring masked values and all kinds of edge cases to make a
        # simple example...
        x, y = [self.vmin, self.midpoint, self.vmax], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(value, x, y), np.isnan(value))

################################################################################


x,y = np.linspace(-10, 10, 100), np.linspace(-10, 10, 100)
X, Y = np.meshgrid(x,y)
Z = make_Z(X,Y, offset=0.8)#; print(Z.shape)
tens = np.logspace(0., 3.0, num=4); print(tens)
Zs = [Z/i for i in tens]#; print(len(Zs))
min_Z = np.min(Zs)
max_Z = np.max(Zs)
norm = MidpointNormalize(vmin=min_Z, vmax=max_Z, midpoint=0.)
for Z in Zs:
    print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
    fig, ax = plt.subplots()
    Z_plot = ax.contourf(X,Y,Z, levels=100, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z)
    plt.colorbar(Z_plot, ax=ax)
    plt.show()
    print("-"*70)

这会产生以下输出:

说明:我根据X和Y坐标做一个Z值,然后把每个图中的Z值除以十的幂(100, 101, 102, 103), 因此地块越往下越弱。

基本上,一切都符合预期。但是,我希望将相同的颜色条应用于所有图形。目前看起来应用了相同的颜色图(它应该是),但我想要完整的“蓝色底部红色上方” - 所有图形中的颜色条,即使是那些看起来全是白色的颜色条。我有点自信,我尝试了 contourfcolorbar 的所有参数,包括 vminvmax 参数,但无法显示完整范围的颜色条在所有数字上。有什么提示吗?

编辑:

我想也许可以调整颜色条的 ylim 属性,因为它毕竟只是一个常规轴。然而,将颜色条定义为 cbar 然后执行 cbar.ax.set_ylim(min_Z, max_Z) 会导致乱码。

选项 1

我不知道这是否适合你,但你可以将 contourf 替换为 imshow:

Z_plot = ax.imshow(Z, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z, interpolation = 'bilinear')


选项 2

matplotlib.pyplot.colorbar 链接到您作为第一个参数传递的可映射对象。一个技巧可以是传递给 colorbar 非缩放 contour.
试试这个代码是否适合你:

for Z, ten in zip(Zs, tens):
    print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
    fig, ax = plt.subplots()
    plt.colorbar(ax.contourf(X,Y,Z*ten, levels=100, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z), ax=ax)
    Z_plot = ax.contourf(X, Y, Z, levels = 100, norm = norm, cmap = 'seismic', vmin = min_Z, vmax = max_Z)
    plt.show()
    print("-"*70)


选项 3

与上面相同的概念,但这次我将可映射对象设置为以更简洁的方式传递给 colorbar

for Z in Zs:
    print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
    fig, ax = plt.subplots()
    Z_plot = ax.contourf(X, Y, Z, levels = 100, norm = norm, cmap = 'seismic', vmin = min_Z, vmax = max_Z)
    m = cm.ScalarMappable(cmap = cm.seismic, norm = colors.TwoSlopeNorm(vmin = np.min(Z), vcenter = 0, vmax = np.max(Z)))
    m.set_array(Z)
    m.set_clim(np.min(Z), np.max(Z))
    cbar = plt.colorbar(m, boundaries = np.linspace(np.min(Z), np.max(Z), 100))
    ticks = np.append(0, np.linspace(np.min(Z), np.max(Z), 9))
    ticks.sort()
    cbar.set_ticks(ticks)
    plt.show()
    print("-"*70)

这个答案完全归功于 Zephyr 上面的答案,我接受了第三个代码块,我在下面对其进行了改编,以便在我提供的 MWE 和我的实际代码中使用:

x,y = np.linspace(-10, 10, 100), np.linspace(-10, 10, 100)
X, Y = np.meshgrid(x,y)
Z = make_Z(X,Y, offset=0.8)#; print(Z.shape)
tens = np.logspace(0., 3.0, num=4); print(tens)
Zs = [Z/i for i in tens]#; print(len(Zs))
min_Z = np.min(Zs)
max_Z = np.max(Zs)

for Z in Zs:
    print("min, max: %f, %f"%(np.min(Z), np.max(Z)))
    fig, ax = plt.subplots()
    Z_plot = ax.contourf(X,Y,Z, levels=100, norm=norm, cmap='seismic', vmin=min_Z, vmax=max_Z)
    m = cm.ScalarMappable(cmap = 'seismic', norm = norm)
    cbar = plt.colorbar(m, boundaries = np.linspace(min_Z, max_Z, 100))
    plt.show()
    print("-"*70)

与 Zephyr 答案的差异:

  • 我从他的回答中唯一接手的部分是包含 ScalarMappable 的那一行,我在其中将范数设置为与 contourf[=23= 中使用的范数相同]
  • 关于刻度线的所有内容我都删除了,因为对我来说自动刻度线就足够了。
  • set_arrayset_clim 功能我已经删除了,因为它们对我没有任何改变,坦率地说,我不知道它们有什么用。