【问题标题】:how to extract a subset of a colormap as a new colormap in matplotlib?如何在 matplotlib 中提取颜色图的子集作为新的颜色图?
【发布时间】:2020-03-30 15:16:08
【问题描述】:

我想使用 matplotlib 中的颜色图,例如CMR 地图。但我不想在开头使用“黑色”颜色,在结尾使用“白色”颜色。我有兴趣使用中间颜色绘制我的数据。我认为 ppl 经常使用它,但我在互联网上搜索并且无法找到任何简单的解决方案。如果有人提出任何解决方案,我将不胜感激。

【问题讨论】:

  • 当颜色用于文本背景时,V 有助于避免黑色 - 好问题

标签: python matplotlib


【解决方案1】:

静态方法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 的相同图像。

【讨论】:

  • 我冒昧地将它与 get_cmap、array_cmap、stack_colormap、band_colormap 一起放在 gist.github colormaputil.py 上。
  • 谢谢。如果有人再次查看这个问题,还有一个问题:如果我在离散颜色图上运行 truncate_colormap,我会得到一个平滑的地图作为回报。我将如何返回离散地图?这有点奇怪,因为函数本身返回一个线性分段地图
  • 为什么 n=100 而不是 256?
  • 对于那些像我一样非常困惑的人,对象cmap可以接受参数:类matplotlib.colors.Colormap有一个方法__call__,这使它成为一个函子,即对象可以像函数一样使用。
  • 对于那些寻找@denis 提到的github gist 的人
【解决方案2】:

我最近自己也在为此苦苦挣扎。以下是一些可能的解决方案:


尝试在绘图函数中使用vminvmax 关键字参数。例如,假设您的数据介于 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 之间的值,并且上面和下面的值将分别为青色和洋红色。这可能无法完全解决您的问题,但如果您喜欢特定的颜色图并希望它在两端都有额外的颜色,它可能会很有用。


如果您真的想更改颜色图,请查看文档hereLinearSegmentedColormap here

首先,

import matplotlib.cm as cm
cdict = cm.get_cmap('spectral_r')._segmentdata

这将返回组成颜色图的所有颜色的字典。然而,弄清楚如何修改这本字典是相当棘手的。这个字典有三个键,red, green, bluecdict[key] 返回 (x, y0, y1) 形式的值列表。我们来看看cdict['red']的两个连续元素:

((0.0, 0.0, 0.0)
 (0.5, 1.0, 1.0),...

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

【讨论】:

  • 感谢您提供的信息丰富的答案。构建自定义颜色图将非常有用。
【解决方案3】:

在我的 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)

【讨论】:

  • CMasher 是要走的路。当被问到这个问题时它不可用,但我认为现在它是解决 OPs 问题的最先进的解决方案。
【解决方案4】:

这是对嵌入绘图功能的先前答案的改编:

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

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

【讨论】:

    【解决方案5】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2014-10-14
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多