由于某些未知原因,TwoSlopeNorm 似乎不支持 under 和 over 颜色。将代码更改为使用 plt.Normalize() 而不是 TwoSlopeNorm() 表示对于该规范,under 颜色按预期工作。
解决方法是再次绘制pcolormesh,仅用于under 颜色。缺点是颜色条扩展中没有显示底色。
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, ListedColormap, TwoSlopeNorm
red2orange = np.array([np.linspace(1, 1, 256),
np.linspace(0, 165 / 256, 256),
np.linspace(0, 0, 256),
np.ones(256)]).T
grey2black = np.array([np.linspace(0.75, 0.25, 256),
np.linspace(0.75, 0.25, 256),
np.linspace(0.75, 0.25, 256),
np.ones(256)]).T
all_colors = np.vstack((grey2black, red2orange))
cmap = LinearSegmentedColormap.from_list('two_slope_cmap', all_colors)
divnorm = TwoSlopeNorm(vmin=1, vcenter=400, vmax=1000)
# seting bad and under
cmap.set_bad('mediumspringgreen')
cmap.set_under('dodgerblue') # this doesn't seem to be used with a TwoSlopeNorm
# fake data
data = np.arange(1400)[:, None] * np.ones(200)
data[1100:1150, 50:150] = np.nan # bad data
data[1200:1250, 50:150] = 0 # zero data
data[1300:1350, 50:150] = -1 # under data
# plot
f, a = plt.subplots()
raster = a.pcolormesh(data, cmap=cmap, norm=divnorm)
cbar = f.colorbar(raster, ax=a, extend='both')
# draw the mesh a second time, only for the under color
a.pcolormesh(np.where(data < 1, 0, np.nan), cmap=ListedColormap([cmap.get_under()]))
plt.show()
另一种解决方法是更改颜色图本身,将最低颜色设置为所需的底色。一个缺点是vmin 需要移动一点。 (距离好像是vmin和vcenter之间距离的1/127。在内部,colormap保持256种颜色,vcenter的颜色在位置128。)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap, TwoSlopeNorm, to_rgba
red2orange = np.array([np.linspace(1, 1, 256),
np.linspace(0, 165 / 256, 256),
np.linspace(0, 0, 256),
np.ones(256)]).T
grey2black = np.array([np.linspace(0.75, 0.25, 256),
np.linspace(0.75, 0.25, 256),
np.linspace(0.75, 0.25, 256),
np.ones(256)]).T
all_colors = np.vstack((grey2black, red2orange))
all_colors[0, :] = to_rgba('dodgerblue')
cmap = LinearSegmentedColormap.from_list('two_slope_cmap', all_colors)
vmin = 1
vcenter = 400
vmax = 1000
divnorm = TwoSlopeNorm(vmin=vmin-(vcenter-vmin)/127, vcenter=vcenter, vmax=vmax)
# seting bad and under
cmap.set_bad('mediumspringgreen')
cmap.set_under('red') # this doesn't seem to be used with a TwoSlopeNorm
# fake data
data = np.arange(1400)[:, None] * np.ones(200)
data[1100:1150, 50:150] = np.nan # bad data
data[1200:1250, 50:150] = 0 # zero data
data[1300:1350, 50:150] = -1 # under data
# plot
f, a = plt.subplots()
raster = a.pcolormesh(data, cmap=cmap, norm=divnorm)
cbar = f.colorbar(raster, ax=a, extend='both')
plt.show()
PS:以下代码试图形象化这两种规范如何区别对待under 颜色:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import TwoSlopeNorm
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(10, 3))
data = np.arange(0, 100)[:, None]
cmap = plt.get_cmap('viridis').copy()
cmap.set_under('crimson')
cmap.set_over('skyblue')
norm1 = plt.Normalize(20, 80)
im1 = ax1.imshow(data, cmap=cmap, norm=norm1, aspect='auto', origin='lower', interpolation='nearest')
plt.colorbar(im1, ax=ax1, extend='both')
ax1.set_title('using plt.Normalize()')
norm2 = TwoSlopeNorm(vmin=20, vcenter=30, vmax=80)
im2 = ax2.imshow(data, cmap=cmap, norm=norm2, aspect='auto', origin='lower', interpolation='nearest')
plt.colorbar(im2, ax=ax2, extend='both')
ax2.set_title('using TwoSlopeNorm')
plt.show()
PPS:在 github 上查看 TwoSlopeNorm 的源码,问题似乎在 matplotlib 的下一个版本(当前版本是 3.4.3)中得到解决。因此,您可以尝试安装开发版本。 (更改涉及将left=-np.inf, right=np.inf 作为参数添加到colors.py 中TwoSlopeNorm 类的__call__ 方法中的np.interp。