如何在不均匀的颜色渐变中将0设置为白色

4
我有一个颜色渐变不均匀的问题,我希望将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')
2个回答

5

另一个回答把它弄得比必要的复杂。为了使颜色图的中间点为0,使用DivergingNorm并设置vcenter=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()

enter image description here

注意: 从matplotlib 3.2版本开始,DivergingNorm将更名为TwoSlopeNorm


1
为了获得所需的规范,请在负数和正数两侧设置相同数量的颜色。
或者,您可以使用未修改的规范,并创建特殊的颜色地图。这样的颜色地图将有1/3的蓝白色和2/3的白红色。好处是颜色条看起来更漂亮。这种方法仅在负数和正数之间的平衡不是太极端时才有效。
这是带有生成数据的演示代码。选择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()

example plot

以下代码绘制了范数,看起来像一个阶跃函数。只有在257个边界处,这个阶跃函数才在任何地方具有正确的形状(缩放到x=-2,0和4)。
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')

谢谢你的帮助!我花了一些时间才弄对,但现在它可以工作了。你知道是否可能让负数柱比正数柱短吗? - Rebecca
可以做到。在这种情况下,不需要规范,只需要重新计算颜色映射表即可。如果负数和正数之间存在巨大差异,则效果不佳,但是以2为因子则效果良好。请给我一些时间来创建一个示例。 - JohanC

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接