python - pyplot TwoSlopeNorm LinearSegmentedColormap 中零值的唯一颜色

标签 python matplotlib colormap

我想在数据为零的绘图上使用自定义(蓝色)颜色。我尝试过set_under方法,但失败了。所需的输出将是图形底部的一条蓝线和上部的两个蓝色方 block 。如有任何帮助,我们将不胜感激。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap,TwoSlopeNorm

# TwoSlopeNorm see:
# https://matplotlib.org/devdocs/tutorials/colors/colormapnorms.html#sphx-glr-tutorials-colors-colormapnorms-py
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('blue')

#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')

enter image description here

最佳答案

出于某种未知的原因,TwoSlopeNorm 似乎不支持 underover 颜色。将代码更改为使用 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()

mimicking under color for TwoSlopeNorm by drawing twice

另一个解决方法是更改​​颜色图本身,将最低颜色设置为所需的底色。缺点是 vmin 需要移动一点。 (距离似乎是 vmin 和 vcenter 之间距离的 1/127。在内部,颜色图保持 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()

mimicking set_under for TwoSlopeNorm

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()

TwoSlopeNorm doesn't honor set_under color

PPS:在github上查看TwoSlopeNorm的源代码,问题似乎在matplotlib的下一个版本(当前版本是3.4.3)中得到了解决。因此,您可以尝试安装开发版本。 (此更改涉及将 left=-np.inf, right=np.inf 添加为参数到 __call__ 方法中的 np.interp colors.py 中的 TwoSlopeNorm 类。

关于python - pyplot TwoSlopeNorm LinearSegmentedColormap 中零值的唯一颜色,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69351535/

相关文章:

python-2.7 - 保存 matplotlib 3d 旋转图

matlab - 如何为 3D 条形图中的条设置任意颜色?

python - 根据颜色图在条形图中的 y 值

python - 无法在 GPU 上加载 "TheBloke/Mistral-7B-v0.1-GGUF"模型

python - 递减范围内的乘积之和

python - Seaborn:从 distplot 中删除拟合

python - matplotlib:减少子图中的轴宽度

python - Django ORM 和 Django REST 框架 : How to make "grouped" arrays?

python - QImage : URL instead of file path?

python - 箱线图中组之间的自定义间距