Python 如何在matplotlib中将颜色映射的子集提取为新的颜色映射?

Python 如何在matplotlib中将颜色映射的子集提取为新的颜色映射?,python,matplotlib,Python,Matplotlib,我想使用matplotlib中的颜色映射,例如CMRmap。但我不想在开头使用“黑色”,在结尾使用“白色”。我对使用中间颜色绘制数据感兴趣。我想ppl经常使用它,但我在网上搜索,找不到任何简单的解决方案。如果有人提出任何解决方案,我将不胜感激。我最近正独自努力解决这个问题。以下是一些可能的解决方案: 尝试在绘图函数中使用vmin,vmax关键字参数。例如,假设您的数据介于0和1之间,但不喜欢在0和1的颜色贴图的极端使用的颜色 import matplotlib.pyplot as plt im

我想使用matplotlib中的颜色映射,例如CMRmap。但我不想在开头使用“黑色”,在结尾使用“白色”。我对使用中间颜色绘制数据感兴趣。我想ppl经常使用它,但我在网上搜索,找不到任何简单的解决方案。如果有人提出任何解决方案,我将不胜感激。

我最近正独自努力解决这个问题。以下是一些可能的解决方案:


尝试在绘图函数中使用
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之间的值,并且高于和低于此值的值将分别为青色和洋红。这可能不能完全解决您的问题,但如果您喜欢某个特定的颜色贴图,并且希望它的两端都有额外的颜色,那么它可能会很有用


如果您真的想更改颜色映射,请查看文档并获取详细信息

首先,

import matplotlib.cm as cm
cdict = cm.get_cmap('spectral_r')._segmentdata
这将返回组成colormap的所有颜色的字典。然而,弄清楚如何修改这本词典是相当棘手的。此命令有三个键,
红色、绿色、蓝色
cdict[key]
返回一个值列表,其格式为
(x,y0,y1)
。让我们来看看两个连续的元素<代码> cDAT[(红色)] < /代码>:

((0.0, 0.0, 0.0)
 (0.5, 1.0, 1.0),...
这意味着
z
(假设我们正在执行
pcolor
imshow
)介于0.0和0.5之间的数据,与该数据相关的rgb颜色的红色分量将从0.0(无红色)增加到1.0(最大红色)。这意味着要更改colormap的颜色,您必须检查rgb的三个分量中的每一个是如何在您感兴趣的colormap区域内插值的只需确保每个颜色的第一个和最后一个条目分别以
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


我想做的是将
spectrum\u r
colormap末尾的灰色更改为纯白色。这是通过使用

# 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)
staticmethod可用于创建新的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\u cmap

的相同图像。这是对先前答案的改编,其中嵌入了绘图功能:

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


如果您需要多次调用该函数,则使用内嵌绘图的紧凑函数是很有帮助的。

从一个(受答案启发的)函数中稍微改进了可视化效果


在我的CMasher软件包中,我提供了
get\u sub\u cmap()
-函数(),它接受一个颜色映射和一个范围,并返回一个包含请求范围的新颜色映射

因此,例如,如果您希望采用
viridis
colormap的20%到80%之间的颜色,您可以使用:

将cmasher导入为cmr
cmap=cmr.get_sub_cmap('viridis',0.2,0.8)
注意:不要使用
jet
(或
CMRmap
),因为它们在感知上不是一致的。 相反,请使用matplotlib中的5个正确的颜色贴图,或使用cmocean或my CMasher提供的颜色贴图

编辑:在最新版本的CMasher中,还可以使用相同的函数,通过向函数提供要获取的段数,从任何颜色映射中创建离散/定性颜色映射。 例如,如果要创建20%到80%范围内的
viridis
定性颜色图,可以使用以下方法:

cmap=cmr.get_sub_map('viridis',0.2,0.8,N=5)

感谢您提供了信息丰富的答案。构建自定义颜色映射非常有用。我已经冒昧地在gist.github上与get\u cmap、array\u cmap、stack\u colormap、band\u colormap一起发布了这篇文章。谢谢。如果有人再看一遍,还有一个问题:如果我在离散的颜色映射上运行truncate_colormap,我会得到一个平滑的映射。如何返回离散映射?这有点奇怪,因为函数本身返回一个线性分段映射为什么n=100而不是256?对于那些像我一样非常困惑的人,对象
cmap
可以接受参数:类matplotlib.colors.Colormap有一个方法
\u调用
,这使它成为一个函子,也就是说,对象可以像函数一样使用。对于那些寻找@denisV提到的颜色的人来说,当颜色用于文本背景时,避免黑色很有用-好问题
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()