【问题标题】:Unique color for zero values in pyplot TwoSlopeNorm LinearSegmentedColormappyplot TwoSlopeNorm LinearSegmentedColormap 中零值的唯一颜色
【发布时间】:2021-11-19 21:27:16
【问题描述】:

我想在数据为零的绘图上使用自定义(蓝色)颜色。我尝试了 set_under 方法,但失败了。所需的输出将是底部的一条蓝线和图形上部的两个蓝色方块。任何帮助表示赞赏。

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

【问题讨论】:

  • 不知什么原因,TwoSlopeNorm 似乎不支持underover 颜色。

标签: python matplotlib colormap


【解决方案1】:

由于某些未知原因,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()

另一种解决方法是更改​​颜色图本身,将最低颜色设置为所需的底色。一个缺点是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.pyTwoSlopeNorm 类的__call__ 方法中的np.interp

【讨论】:

  • 感谢您的详尽回答!使用您的解决方法,任何低于 4 的值都变为蓝色,而不仅仅是预期的零或更小的值。 (尝试:data[1300:1350, 50:150] = 4 # under data。)我可以通过将 vmin 设置为 -3 来神奇地纠正这种行为(divnorm = TwoSlopeNorm(vmin=-3, vcenter=400, vmax=1000) ,但我不知道这个魔法偏移器的起源。我有点害怕,如果我生成几千个不同数据范围和 TwoSlopeNorm 参数的图,它会产生不一致的结果。请您在关闭前评论我的担忧这个问题完全解决了吗?
  • 我用第二次绘制网格的方法更新了答案。还有另一种移动 vmin 的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-21
相关资源
最近更新 更多