Python 修改离散LinearSegmentedColormap

Python 修改离散LinearSegmentedColormap,python,matplotlib,Python,Matplotlib,我是一名气候学家,经常使用“蓝-白-红”彩色地图绘制温度场等异常情况。为了使绘图更具可读性,我使用我在互联网上“找到”的一个函数(但我并不真正理解它),将彩色地图离散化为一定数量的级别(箱): 大概是这样的: import matplotlib.pyplot as plt import numpy as np from matplotlib import cm import matplotlib.colors as cols from numpy.random import randn def

我是一名气候学家,经常使用“蓝-白-红”彩色地图绘制温度场等异常情况。为了使绘图更具可读性,我使用我在互联网上“找到”的一个函数(但我并不真正理解它),将彩色地图离散化为一定数量的级别(箱):

大概是这样的:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib.colors as cols
from numpy.random import randn

def cmap_discretize(cmap, N):
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = {}
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
    # Return colormap object.
    return cols.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)



cmap_disc= cmap_discretize(cm.RdBu_r,12)


fig, ax = plt.subplots()
data = np.clip(randn(250, 250), -1, 1)

cax = ax.pcolor(data, cmap=cmap_disc)
plt.colorbar(cax)

plt.show()
这导致

现在我想将最中间的两段(即接近0的那两段)设置为白色,因为我不想显示非常小的偏差

我的目标是以类似的方式结束:


我真的很难弄清楚这些LinearSegmentedColormap是如何相应地修改的。有人能帮我吗

您找到的函数构建了一个数据结构(在
cdict
中),用于定义不执行任何插值的段(即,行
i
中的
y1
始终与行
i+1
中的
y0
相同,这给出了恒定或离散的颜色“带”)

cdict
是一个奇怪的数据结构,一个包含键
'red'
'green'
'blue'
的字典。每个键的值都是一个列表结构,包含
(x,y0,y1)
形式的元组
x
是颜色贴图坐标,它是介于0和1之间的某个浮点数
y0
x
左侧的颜色值,
y1
x
右侧的颜色值。颜色在
x
的连续值之间线性插值;如果第一个元组由
(0,A,B)
给出,第二个元组由
(X,C,D)
给出,那么
0
X
之间的点
t
的颜色将由
(t-0)/(X-0)*(C-B)+B给出

出于您的目的,您的函数运行得很好,但需要将颜色贴图中间附近的“条带”替换为白色。您可以尝试以下方法:

def cmap_discretize(cmap, N):
    colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
    colors_rgba = cmap(colors_i)
    indices = np.linspace(0, 1., N+1)
    cdict = {}
    for ki,key in enumerate(('red','green','blue')):
        cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
    # "white out" the bands closest to the middle
    num_middle_bands = 2 - (N % 2)
    middle_band_start_idx = (N - num_middle_bands) // 2
    for middle_band_idx in range(middle_band_start_idx,
                                 middle_band_start_idx + num_middle_bands):
        for key in cdict.keys():
            old = cdict[key][middle_band_idx]
            cdict[key][middle_band_idx] = old[:2] + (1.,)
            old = cdict[key][middle_band_idx + 1]
            cdict[key][middle_band_idx + 1] = old[:1] + (1.,) + old[2:]
    # Return colormap object.
    return cols.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

让我们从浏览已有的代码开始

# get some uniformly sampled data, padded out a bit
colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
# sample the input colormap at our sample points
colors_rgba = cmap(colors_i)
# indices for color map
indices = np.linspace(0, 1., N+1)
# dict to pass to the LinearSegmentedColormap
cdict = {}
# loop over the colors
for ki,key in enumerate(('red','green','blue')):
    # in each color assemble a list that looks like
    #[...,
    # (indices[2], colors_rgba[1,ki], colors_rgba[2,ki]),
    # (indices[3], colors_rgba[2,ki], colors_rgba[3,ki]),
    # ....]
    cdict[key] = [ (indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1) ]
    # The color for a number between [indices[2], indices[3]] are interpolated
    # between colors_rgba[2,ki] and colors_rgba[2,ki] which are the same
    # which is what gives you the discrete blocks.
# Construct and return colormap object.
return cols.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)
现在的问题是如何在中间创建一个“双”白色带的彩色地图。我会更改函数位,使其包含两个颜色贴图(顶部和底部)

导入matplotlib.pyplot作为plt
将numpy作为np导入
从matplotlib导入cm
将matplotlib.colors导入为cols
从numpy.random导入randn
def cmap_double_离散化(cmap_底部,cmap_顶部,N,分割=.5):
"""
使用两个现有颜色贴图生成已描述的颜色贴图
参数
----------
cmap_底部:cmap
底部cmap
cmap_顶部:cmap
顶级cmap
N:int
每个颜色贴图中的存储箱数
拆分:浮动,可选
加入地图的位置必须在[0,1]中
"""
#健康检查
断言split<1和split>0
#建立数据结构
cdict={lab:[]用于实验室输入('red'、'green'、'blue'))
#在一个奇妙的循环中这样做,a)节省打字,b)使操作变得容易
#改装以进行任意拆分
对于cmap,在拉链中结束((cmap_底部,cmap_顶部),((0,拆分),(拆分,1)):
#为每个颜色贴图运行整个范围
颜色i=np.连接((np.linspace(0,1,N),(0,0,0,0.))
#映射颜色
颜色\u rgba=cmap(颜色\u i)
#获取值
索引=np.linspace(结束[0],结束[1],N+1,结束=True)
对于ki,输入枚举(‘红色’、‘绿色’、‘蓝色’):
cdict[key].xrange(N+1)中i的扩展((索引[i],颜色[i-1,ki],颜色[i,ki]))
#打印cdict
#返回colormap对象。
返回cols.LinearSegmentedColormap(cmap.name+“u%d”%N,cdict,1024)
红色=红色:[(0,0,1),
(1, 1, 0)],
“蓝色”:[(0,0,0),
(1, 1, 0)],
“绿色”:[(0,0,0),
(1, 1, 0)]}
蓝色(cdict={'blue':[(0,0,1),
(1, 1, 0),],
“红色”:[(0,0,1),
(1, 0, 0)],
“绿色”:[(0,0,1),
(1, 0, 0)]}
红色\u cmap=cols.LinearSegmentedColormap('red',红色\u cdict,1024)
blue\u cmap=cols.LinearSegmentedColormap('blue',blue\u cdict,1024)
测试cmap=cmap双离散化(红色cmap,蓝色cmap,6)
#这些不是真的变成白色的!
#test\u cmap=cmap\u double\u离散化(cm.get\u cmap('Reds\u r'),cm.get\u cmap('Blues'),6)
图,ax=plt.子批次()
数据=np.clip(随机数(250250),-1,1)
cax=ax.pcolor(数据,cmap=test\u cmap)
打印颜色条(cax)
plt.show()


您可以很容易地修改它,将其拆分为两个以上的颜色贴图。

请阅读:它清楚地描述了颜色贴图的工作原理。使用代码并传入两个描述的颜色贴图,您可能会得到一些启发。这很好,但我仍然需要通过它来理解ITI有点轻而易举地达到这样的效果,因为你正在用一种非透明的方式使彩色地图非线性。这也是有效的,但不支持中间有白色的预定义彩色图(如BWR、地震、RdBu、RdGy……)。还是非常感谢你的努力
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
import matplotlib.colors as cols
from numpy.random import randn

def cmap_double_discretize(cmap_bottom, cmap_top, N, split=.5):
    """
    Generates a descritized color map using two existing color maps

    Parameters
    ----------
    cmap_bottom : cmap
        The bottom cmap

    cmap_top : cmap
        The top cmap

    N : int
       The number of bins in each color map

    split : float, optional
       Where to join the maps, must be in [0, 1]
    """
    # sanity check
    assert split < 1 and split > 0
    # set up the data structure
    cdict = {lab: [] for lab in ('red','green','blue')}
    # do this in a fancy loop to a) save typing, b) make it easy to
    # retrofit to do arbitrary splits
    for cmap, ends in zip((cmap_bottom, cmap_top), ((0, split), (split, 1))):

        # run over the _whole_ range for each color map
        colors_i = np.concatenate((np.linspace(0, 1., N), (0.,0.,0.,0.)))
        # map the color
        colors_rgba = cmap(colors_i)
        # get the values 
        indices = np.linspace(ends[0], ends[1], N+1, endpoint=True)

        for ki,key in enumerate(('red','green','blue')):
            cdict[key].extend((indices[i], colors_rgba[i-1,ki], colors_rgba[i,ki]) for i in xrange(N+1))
            #    print cdict
    # Return colormap object.
    return cols.LinearSegmentedColormap(cmap.name + "_%d"%N, cdict, 1024)

red_cdict = {'red': [(0, 0, 1),
                     (1, 1, 0)],
            'blue': [(0, 0, 0),
                     (1, 1, 0)],
            'green': [(0, 0, 0),
                     (1, 1, 0)]}

blue_cdict = {'blue': [(0, 0, 1),
                       (1, 1, 0),],
            'red': [(0, 0, 1),
                    (1, 0, 0)],
            'green': [(0, 0, 1),
                     (1, 0, 0)]}
red_cmap = cols.LinearSegmentedColormap('red', red_cdict, 1024)
blue_cmap = cols.LinearSegmentedColormap('blue', blue_cdict, 1024)

test_cmap = cmap_double_discretize(red_cmap, blue_cmap, 6)
# these don't actually go to white!
# test_cmap = cmap_double_discretize(cm.get_cmap('Reds_r'), cm.get_cmap('Blues'), 6)


fig, ax = plt.subplots()
data = np.clip(randn(250, 250), -1, 1)

cax = ax.pcolor(data, cmap=test_cmap)
plt.colorbar(cax)

plt.show()