如何在不均匀的色带上将 0 设置为白色?

How to set 0 to white at a uneven color ramp?

我的色带不均匀,我希望 0 为白色。所有的负色都必须是蓝色的,所有的正色都必须是红色的。 我目前的尝试显示 0 蓝色和 0.7 白色。

有办法把0设置成白色吗?

import numpy as np
import matplotlib.colors as colors 
from matplotlib import pyplot as m

bounds_min = np.arange(-2, 0, 0.1)
bounds_max = np.arange(0, 4.1, 0.1)
bounds = np.concatenate((bounds_min, bounds_max), axis=None)       
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)      # I found this on the internet and thought this would solve my problem. But it doesn't...
m.pcolormesh(xx, yy, interpolated_grid_values, norm=norm, cmap='RdBu_r')

为了获得所需的范数,将负片的颜色数设置为与正片相同。

或者,您可以使用未修改的范数,并创建一个特殊的颜色图。这样的颜色图将具有 1/3rd 的蓝到白颜色和 2/3rd 的白到红颜色。一个好处是颜色条看起来更好。这种方法只有在负数和正数之间的平衡不太极端时才有效。

这是带有生成数据的演示代码。 zz 被选择为围绕中心旋转的正弦,并缩放为从 -2 到 4,因此围绕 1 对称。左侧图像显示了修改后的颜色图。在右侧,范数更改为强制白色为零。

由于所有正值都是红色,因此红色波段比蓝色波段宽。在不改变规范或颜色图的图像中,条带将具有相同的宽度。颜色条表示零为白色。

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

x = np.linspace(-20, 20, 500)
y = np.linspace(-20, 20, 500)
xx, yy = np.meshgrid(x, y)
zz = np.sin(np.sqrt(xx * xx + yy * yy)) * 3 + 1

negatives = -2.0
positives = 4.0

bounds_min = np.linspace(negatives, 0, 129)
bounds_max = np.linspace(0, positives, 129)[1:]
    # the zero is only needed once
    # in total there will be 257 bounds, so 256 bins
bounds = np.concatenate((bounds_min, bounds_max), axis=None)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)

num_neg_colors = int(256 / (positives - negatives) * (-negatives))
num_pos_colors = 256 - num_neg_colors
cmap_BuRd = plt.cm.RdBu_r
colors_2neg_4pos = [cmap_BuRd(0.5*c/num_neg_colors) for c in range(num_neg_colors)] +\
                   [cmap_BuRd(1-0.5*c/num_pos_colors) for c in range(num_pos_colors)][::-1]
cmap_2neg_4pos = colors.LinearSegmentedColormap.from_list('cmap_2neg_4pos', colors_2neg_4pos, N=256)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))

mesh1 = ax1.pcolormesh(xx, yy, zz, cmap=cmap_2neg_4pos)
ax1.set_aspect('equal')
ax1.set_title('using a modified cmap')
fig.colorbar(mesh1, ax=ax1)

mesh2 = ax2.pcolormesh(xx, yy, zz, norm=norm, cmap='RdBu_r')
ax2.set_aspect('equal')
ax2.set_title('using a special norm')
ticks = np.append(np.arange(-2.0, 0, 0.25), np.arange(0, 4.001, 0.5))
fig.colorbar(mesh2, ax=ax2, ticks=ticks)

plt.show()

以下代码绘制了范数,它看起来像一个阶梯函数。仅在 257 个边界时,此阶跃函数在任何地方都具有正确的形状(缩放到 -2、0 和 4 处的 x)。

nx = np.linspace(-3,5,10000)
plt.plot(nx, norm(nx))

PS: 有一个来创建一个类似的颜色图。但是尝试一下,很明显 RdBu 颜色图经过微调并生成了更好看的图。

norm_2neg_4pos = mcolors.Normalize(negatives, positives)
colors_2neg_4pos = [[0, 'blue'],
                    [norm_2neg_4pos(0.0), "white"],
                    [1, 'red']]
cmap_2neg_4pos = mcolors.LinearSegmentedColormap.from_list("", colors_2neg_4pos)

另一个简单的解决方案是重新调整 -4 和 4 之间的所有内容。但是,这会丢失较深的蓝色。 'RdBu_r' 的另一种选择是 'seismic',它与 运行 从红色到白色到蓝色的不同方式。

ax.pcolormesh(xx, yy, zz, vmin=-positives, vmax=positives, cmap='RdBu_r')

另一个答案使它比需要的复杂一点。为了使颜色图的中间点为 0,请使用 DivergingNormvcenter=0

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

x, y = np.meshgrid(np.linspace(0,50,51), np.linspace(0,50,51))
z = np.linspace(-2,4,50*50).reshape(50,50)

norm = DivergingNorm(vmin=z.min(), vcenter=0, vmax=z.max())
pc = plt.pcolormesh(x,y,z, norm=norm, cmap="RdBu_r")
plt.colorbar(pc)

plt.show()

注意:从matplotlib 3.2开始DivergingNorm将重命名为TwoSlopeNorm