【发布时间】:2020-03-30 15:16:08
【问题描述】:
我想使用 matplotlib 中的颜色图,例如CMR 地图。但我不想在开头使用“黑色”颜色,在结尾使用“白色”颜色。我有兴趣使用中间颜色绘制我的数据。我认为 ppl 经常使用它,但我在互联网上搜索并且无法找到任何简单的解决方案。如果有人提出任何解决方案,我将不胜感激。
【问题讨论】:
-
当颜色用于文本背景时,V 有助于避免黑色 - 好问题
标签: python matplotlib
我想使用 matplotlib 中的颜色图,例如CMR 地图。但我不想在开头使用“黑色”颜色,在结尾使用“白色”颜色。我有兴趣使用中间颜色绘制我的数据。我认为 ppl 经常使用它,但我在互联网上搜索并且无法找到任何简单的解决方案。如果有人提出任何解决方案,我将不胜感激。
【问题讨论】:
标签: python matplotlib
静态方法colors.LinearSegmentedColormap.from_list 可用于创建新的LinearSegmentedColormaps。下面,我在 0.2 和 0.8 之间的 100 个点处对原始颜色图进行采样:
cmap(np.linspace(0.2, 0.8, 100))
并使用这些颜色生成新的颜色图:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
new_cmap = colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
cmap(np.linspace(minval, maxval, n)))
return new_cmap
arr = np.linspace(0, 50, 100).reshape((10, 10))
fig, ax = plt.subplots(ncols=2)
cmap = plt.get_cmap('jet')
new_cmap = truncate_colormap(cmap, 0.2, 0.8)
ax[0].imshow(arr, interpolation='nearest', cmap=cmap)
ax[1].imshow(arr, interpolation='nearest', cmap=new_cmap)
plt.show()
左边的图显示了使用原始颜色图的图像(在本例中为jet)。右图显示了使用new_cmap 的相同图像。
【讨论】:
cmap可以接受参数:类matplotlib.colors.Colormap有一个方法__call__,这使它成为一个函子,即对象可以像函数一样使用。
我最近自己也在为此苦苦挣扎。以下是一些可能的解决方案:
尝试在绘图函数中使用vmin、vmax 关键字参数。例如,假设您的数据介于 0 和 1 之间,但不喜欢 0 和 1 的颜色图的极端颜色。
import matplotlib.pyplot as plt
import matplotlib.cm as cm
my_cmap = cm.spectral_r
my_cmap.set_over('c')
my_cmap.set_under('m')
plt.pcolor(data, vmin=0.01, vmax=0.99, cmap=my_cmap)
这将强制将整个颜色图用于 0.01 和 0.99 之间的值,并且上面和下面的值将分别为青色和洋红色。这可能无法完全解决您的问题,但如果您喜欢特定的颜色图并希望它在两端都有额外的颜色,它可能会很有用。
如果您真的想更改颜色图,请查看文档here 和LinearSegmentedColormap here。
首先,
import matplotlib.cm as cm
cdict = cm.get_cmap('spectral_r')._segmentdata
这将返回组成颜色图的所有颜色的字典。然而,弄清楚如何修改这本字典是相当棘手的。这个字典有三个键,red, green, blue。 cdict[key] 返回 (x, y0, y1) 形式的值列表。我们来看看cdict['red']的两个连续元素:
((0.0, 0.0, 0.0)
(0.5, 1.0, 1.0),...
这意味着z(假设我们正在执行pcolor 或imshow)在0.0 和0.5 之间的数据将具有与该数据相关联的rgb 颜色的红色分量将从0.0(无红色)到 1.0(最大红色)。这意味着要更改颜色图的颜色,您必须检查 rgb 的三个分量中的每一个是如何在您感兴趣的颜色图区域内插值的。只需确保对于每种颜色,第一个最后一个条目分别以x=0 和x=1 开头;您必须覆盖 [0, 1] 的整个范围。
如果你想改变开始和结束的颜色,试试
import matplotlib.cm as cm
from matplotlib.colors import LinearSegmentedColormap
cdict = cm.get_cmap('spectral_r')._segmentdata
cdict['red'][0] = (0, 0.5, 0.5) # x=0 for bottom color in colormap
cdict['blue'][0] = (0, 0.5, 0.5) # y=0.5 gray
cdict['green'][0] = (0, 0.5, 0.5) # y1=y for simple interpolation
cdict['red'][-1] = (1, 0.5, 0.5) # x=1 for top color in colormap
cdict['blue'][-1] = (1, 0.5, 0.5)
cdict['green'][-1] = (1, 0.5, 0.5)
my_cmap = LinearSegmentedColormap('name', cdict)
然后在你的绘图函数中使用这个 cmap。
我想做的是将spectral_r 颜色图末尾的灰色更改为纯白色。这是使用
# Using imports from above
cdict = matplotlib.cm.get_cmap('spectral_r')._segmentdata
cdict['red'][0] = (0, 1, 1)
cdict['green'][0] = (0, 1, 1)
cdict['blue'][0] = (0, 1, 1)
my_cmap = LinearSegmentedColormap('my_cmap', cdict)
【讨论】:
在我的 CMasher 包中,我提供了 get_sub_cmap()-函数 (https://cmasher.readthedocs.io/user/usage.html#sub-colormaps),它接受一个颜色图和一个范围,并返回一个包含请求范围的新颜色图。
因此,例如,如果您想获取 viridis 颜色图的 20% 到 80% 之间的颜色,您可以这样做:
import cmasher as cmr
cmap = cmr.get_sub_cmap('viridis', 0.2, 0.8)
PS:不要使用jet(或CMRmap),因为它们在感知上不是统一的顺序。
相反,请使用 matplotlib 中的 5 个正确颜色图或 cmocean 或我的 CMasher 提供的颜色图。
编辑:在最新版本的 CMasher 中,您还可以使用相同的函数从任何颜色图创建离散/定性颜色图,方法是向该函数提供要采用的段数。
例如,如果您想在 20% 到 80% 范围内创建viridis 的定性颜色图,您可以这样做:
cmap = cmr.get_sub_map('viridis', 0.2, 0.8, N=5)
【讨论】:
这是对嵌入绘图功能的先前答案的改编:
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import numpy as np
################### Function to truncate color map ###################
def truncate_colormap(cmapIn='jet', minval=0.0, maxval=1.0, n=100):
'''truncate_colormap(cmapIn='jet', minval=0.0, maxval=1.0, n=100)'''
cmapIn = plt.get_cmap(cmapIn)
new_cmap = colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmapIn.name, a=minval, b=maxval),
cmapIn(np.linspace(minval, maxval, n)))
arr = np.linspace(0, 50, 100).reshape((10, 10))
fig, ax = plt.subplots(ncols=2)
ax[0].imshow(arr, interpolation='nearest', cmap=cmapIn)
ax[1].imshow(arr, interpolation='nearest', cmap=new_cmap)
plt.show()
return new_cmap
cmap_mod = truncate_colormap(minval=.2, maxval=.8) # calls function to truncate colormap
如果您需要多次调用该函数,则嵌入绘图的紧凑函数会很有帮助。
【讨论】:
previous answer 的可视化略有改进,(灵感来自 that 答案)
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
def truncate_colormap(cmap, minval=0.0, maxval=1.0, n=100):
'''
https://stackoverflow.com/a/18926541
'''
if isinstance(cmap, str):
cmap = plt.get_cmap(cmap)
new_cmap = mpl.colors.LinearSegmentedColormap.from_list(
'trunc({n},{a:.2f},{b:.2f})'.format(n=cmap.name, a=minval, b=maxval),
cmap(np.linspace(minval, maxval, n)))
return new_cmap
cmap_base = 'jet'
vmin, vmax = 0.2, 0.8
cmap = truncate_colormap(cmap_base, vmin, vmax)
fig, ax = plt.subplots(nrows=2)
sm = mpl.cm.ScalarMappable(cmap=cmap_base)
cbar = plt.colorbar(sm, cax=ax[0], orientation='horizontal')
sm = mpl.cm.ScalarMappable(cmap=cmap)
cbar = plt.colorbar(sm, cax=ax[1], orientation='horizontal')
plt.show()
【讨论】: