简体   繁体   English

如何在 matplotlib 中提取颜色图的子集作为新的颜色图?

[英]how to extract a subset of a colormap as a new colormap in matplotlib?

I would like to use a colormap from matplotlib eg CMRmap.我想使用 matplotlib 中的颜色图,例如 CMRmap。 But I don't want to use the "black" color at the beginning and the "white" color at the end.但我不想在开头使用“黑色”颜色,最后使用“白色”颜色。 I'm interested to plot my data using the in-between colors.我对使用中间 colors 的数据感兴趣。 I think ppl use it quite often but I was searching over internet and could not manage to find any simple solution.我认为 ppl 经常使用它,但我在互联网上搜索并且无法找到任何简单的解决方案。 I'll appreciate if someone suggest any solution.如果有人提出任何解决方案,我将不胜感激。

The staticmethod colors.LinearSegmentedColormap.from_list can be used to create new LinearSegmentedColormaps.静态方法colors.LinearSegmentedColormap.from_list可用于创建新的 LinearSegmentedColormap。 Below, I sample the original colormap at 100 points between 0.2 and 0.8:下面,我在 0.2 和 0.8 之间的 100 个点处对原始颜色图进行采样:

cmap(np.linspace(0.2, 0.8, 100))

and use these colors to generate a new colormap:并使用这些颜色生成新的颜色图:

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

在此处输入图片说明

The plot on the left shows the image using the original colormap (in this example, jet ).左边的图显示了使用原始颜色图(在本例中为jet )的图像。 The plot on the right shows the same image using new_cmap .右侧的图使用new_cmap显示了相同的图像。

I was just recently struggling with this on my own.我最近才独自为此而苦苦挣扎。 Here are some possible solutions:以下是一些可能的解决方案:


Try using vmin , vmax keyword arguments in your plotting function.尝试在绘图函数中使用vminvmax关键字参数。 For example, say you had data between 0 and 1 but didn't like the colors used at the extremes of the colormap for 0 and 1.例如,假设您的数据介于 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)

This will force the entire colormap to be used for values between 0.01 and 0.99 and values above and below will be cyan and magenta respectively.这将强制将整个颜色图用于 0.01 和 0.99 之间的值,并且上面和下面的值将分别是青色和洋红色。 This may not solve your problem exactly, but it could be useful if you like a particular colormap and wish it had additional colors at both ends.这可能不能完全解决您的问题,但如果您喜欢特定的颜色图并希望它的两端都有额外的颜色,它可能会很有用。


If you really want to change the colormap, look at the documentation here and for LinearSegmentedColormap here .如果您真的想更改颜色图,请查看 此处的文档和 此处LinearSegmentedColormap

First,第一的,

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

This returns a dictionary of all the colors that make up the colormap.这将返回组成颜色图的所有颜色的字典。 However, it's pretty tricky figuring out exactly how to alter this dictionary.然而,弄清楚如何修改这本字典是非常棘手的。 This dict has three keys, red, green, blue .这个 dict 有三个键, red, green, blue cdict[key] returns a list of values of the form (x, y0, y1) . cdict[key]返回形式为(x, y0, y1)的值列表。 Let's take a look at two consecutive elements of cdict['red'] :我们来看看cdict['red']两个连续元素:

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

What this means is that data with z (assuming we're doing a pcolor or imshow ) between 0.0 and 0.5 will have the red component of the rgb color associated with that data will increase from 0.0 (no red) to 1.0 (maximum red).这意味着z (假设我们正在执行pcolorimshow )在 0.0 和 0.5 之间的数据将具有与该数据关联的 rgb 颜色的红色分量将从 0.0(无红色)增加到 1.0(最大红色) . This means that to change the color of the colormap, you have to examine how each of the three components of rgb are interpolated in the region of the colormap that you are interested in. Just make sure that for each color, the first and the last entry start with x=0 and x=1 respectively;这意味着要更改颜色图的颜色,您必须检查 rgb 的三个分量中的每一个如何插入您感兴趣的颜色图区域。只需确保对于每种颜色,第一个和最后一个条目分别以x=0x=1开始; you must cover the whole spectrum of [0, 1].您必须涵盖 [0, 1] 的整个范围。

If you want to change the beginning and end colors, try如果要更改开始和结束颜色,请尝试

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)

Then use this cmap in your plotting function.然后在绘图函数中使用此 cmap。


What I wanted to do was change the gray at the end of the spectral_r colormap to pure white.我想要做的是将spectral_r _r 颜色图末尾的灰色更改为纯白色。 This was achieved using这是使用

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

In my CMasher package, I provide the get_sub_cmap() -function ( https://cmasher.readthedocs.io/user/usage.html#sub-colormaps ), which takes a colormap and a range, and returns a new colormap containing the requested range.在我的 CMasher 包中,我提供了get_sub_cmap()函数( https://cmasher.readthedocs.io/user/usage.html#sub-colormaps ),它接受一个颜色图和一个范围,并返回一个包含请求的范围。

So, for example, if you want to take the colors between 20% and 80% of the viridis colormap, you can do that with:因此,例如,如果您想采用viridis颜色图的 20% 到 80% 之间的颜色,您可以这样做:

import cmasher as cmr

cmap = cmr.get_sub_cmap('viridis', 0.2, 0.8)

PS: Do not use jet (or CMRmap ), as they are not perceptually uniform sequential. PS:不要使用jet (或CMRmap ),因为它们在感知上不是统一的顺序。 Instead, use the 5 proper colormaps in matplotlib or the colormaps provided by cmocean or my CMasher.相反,使用 matplotlib 中的 5 个适当的颜色图或由 cmocean 或我的 CMasher 提供的颜色图。

EDIT: In the latest version of CMasher, one can also use this same function to create a discrete/qualitative colormap out of any colormap by supplying the function with the number of segments to take.编辑:在最新版本的 CMasher 中,还可以使用相同的函数通过为函数提供要采用的段数来从任何颜色图中创建离散/定性颜色图。 For example, if you want to create a qualitative colormap of viridis in the 20% to 80% range, you can do this with:例如,如果您想在 20% 到 80% 范围内创建viridis的定性颜色图,您可以这样做:

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

Here is an adaptation of a previous answer which embeds the plotting function:这是对嵌入绘图功能的先前答案的改编:

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

truncate_colormap() 输出的视觉效果:

Having a compact function with the plotting embedded is helpful if you need to call the function more than once.如果您需要多次调用该函数,则具有嵌入绘图的紧凑函数会很有帮助。

Slight improvement of visualization from a previous answer , (inspired by that answer)先前答案的可视化略有改进,(受答案启发)

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

在此处输入图片说明

Quick Wrapper Function:快速包装 Function:

def sub_cmap(cmap, vmin, vmax):
    return lambda v: cmap(vmin + (vmax - vmin) * v)

Usage:用法:

cmap = matplotlib.cm.get_cmap('viridis') # Get your favorite cmap
new_cmap = sub_cmap(cmap, 0.2, 0.9)

# Do plot or something
# ...

"cmap = cmr.get_sub_map('viridis', 0.2, 0.8, N=5)" proposed by @1313e must be the most elegant solution. @1313e 提出的“cmap = cmr.get_sub_map('viridis', 0.2, 0.8, N=5)” 一定是最优雅的解决方案。 But the new function is "cmr.get_sub_cmap()", just replace it.但是新的function是“cmr.get_sub_cmap()”,替换就行了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM