强基初中数学&学Python——第281课 数字和数学第三方模块Matplotlib之九:颜色-创建颜色映射

  Matplotlib有许多内置的颜色映射,可通过Matplotlib.colormaps访问。还有一些外部库,如palettable,具有许多额外的颜色映射。  然而,我们通常希望在Matplotlib中创建或操作颜色映射。这可以使用ListedColormapLinearSegmentedColormap类来完成。从外部看,两个colormap类都将0和1之间的值映射为一组颜色。然而,也存在一些细微的差异,其中一些差异后续会讲到。

  在手动创建或操作颜色映射之前,让我们先看看如何从现有的颜色映射类中获取颜色映射及其颜色。

获取颜色映射并访问其值  首先,可以调用Matplotlib.colormaps返回一个颜色映射对象来获得命名的颜色映射,其中大部分列在Matplotlib中的“选择颜色映射”中(Choosing Colormaps in Matplotlib)。内部用于定义颜色映射的颜色列表的长度可以通过Colormap.resampled进行调整。下面我们使用了一个适度的值8,因此没有太多的值可以查看。

· 

· 

· 

· 

· 

· 

import numpy as npimport matplotlib.pyplot as pltimport matplotlib as mplfrom matplotlib.colors import ListedColormap, LinearSegmentedColormap
viridis = mpl.colormaps['viridis'].resampled(8)

  对象viridis是可调用的,当传递0和1之间的浮点值时,会从颜色映射返回RGBA值:

· 

print(viridis(0.56))

  输出:

· 

(0.122312, 0.633153, 0.530398, 1.0)


列表颜色映射(ListedColormap)  ListedColormaps将其颜色值存储在.colors属性中。可以使用colors属性直接访问包括颜色映射的颜色列表,或者可以用匹配颜色映射长度的数组来调用viridis间接访问该列表。请注意,返回的列表是RGBA Nx4数组的形式,其中N是颜色映射的长度。

· 

· 

· 

print('viridis.colors', viridis.colors)print('viridis(range(8))', viridis(range(8)))print('viridis(np.linspace(0, 1, 8))', viridis(np.linspace(0, 1, 8)))

  输出:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

viridis.colors [[0.267004 0.004874 0.329415 1.      ] [0.275191 0.194905 0.496005 1.      ] [0.212395 0.359683 0.55171  1.      ] [0.153364 0.497    0.557724 1.      ] [0.122312 0.633153 0.530398 1.      ] [0.288921 0.758394 0.428426 1.      ] [0.626579 0.854645 0.223353 1.      ] [0.993248 0.906157 0.143936 1.      ]]viridis(range(8)) [[0.267004 0.004874 0.329415 1.      ] [0.275191 0.194905 0.496005 1.      ] [0.212395 0.359683 0.55171  1.      ] [0.153364 0.497    0.557724 1.      ] [0.122312 0.633153 0.530398 1.      ] [0.288921 0.758394 0.428426 1.      ] [0.626579 0.854645 0.223353 1.      ] [0.993248 0.906157 0.143936 1.      ]]viridis(np.linspace(0, 1, 8)) [[0.267004 0.004874 0.329415 1.      ] [0.275191 0.194905 0.496005 1.      ] [0.212395 0.359683 0.55171  1.      ] [0.153364 0.497    0.557724 1.      ] [0.122312 0.633153 0.530398 1.      ] [0.288921 0.758394 0.428426 1.      ] [0.626579 0.854645 0.223353 1.      ] [0.993248 0.906157 0.143936 1.      ]]

  颜色映射是一个查找表,因此“过采样”颜色映射会返回最近邻插值(请注意下面列表中的重复颜色)。

· 

print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))

  输出:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1.      ] [0.267004 0.004874 0.329415 1.      ] [0.275191 0.194905 0.496005 1.      ] [0.212395 0.359683 0.55171  1.      ] [0.212395 0.359683 0.55171  1.      ] [0.153364 0.497    0.557724 1.      ] [0.122312 0.633153 0.530398 1.      ] [0.288921 0.758394 0.428426 1.      ] [0.288921 0.758394 0.428426 1.      ] [0.626579 0.854645 0.223353 1.      ] [0.993248 0.906157 0.143936 1.      ] [0.993248 0.906157 0.143936 1.      ]]


线性分段颜色映射(LinearSegmentedColormap)  LinearSegmentedColormaps没有.colors属性。然而,仍然可以用整数数组或0到1之间的浮点数组调用颜色映射。

· 

· 

· 

· 

copper = mpl.colormaps['copper'].resampled(8)
print('copper(range(8))', copper(range(8)))print('copper(np.linspace(0, 1, 8))', copper(np.linspace(0, 1, 8)))

  输出:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

copper(range(8)) [[0.         0.         0.         1.        ] [0.17647055 0.1116     0.07107143 1.        ] [0.35294109 0.2232     0.14214286 1.        ] [0.52941164 0.3348     0.21321429 1.        ] [0.70588219 0.4464     0.28428571 1.        ] [0.88235273 0.558      0.35535714 1.        ] [1.         0.6696     0.42642857 1.        ] [1.         0.7812     0.4975     1.        ]]copper(np.linspace(0, 1, 8)) [[0.         0.         0.         1.        ] [0.17647055 0.1116     0.07107143 1.        ] [0.35294109 0.2232     0.14214286 1.        ] [0.52941164 0.3348     0.21321429 1.        ] [0.70588219 0.4464     0.28428571 1.        ] [0.88235273 0.558      0.35535714 1.        ] [1.         0.6696     0.42642857 1.        ] [1.         0.7812     0.4975     1.        ]]


创建列表颜色映射  创建颜色映射本质上与上面的操作相反,我们向ListedColormap提供颜色规格的列表或数组,以创建新的颜色映射。  在继续本教程之前,让我们定义一个辅助函数,该函数以一个或多个颜色映射作为输入,创建一些随机数据,并将颜色映射应用于该数据集的图像中。

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

def plot_examples(colormaps):    """    Helper function to plot data with associated colormap.    """    np.random.seed(19680801)    data = np.random.randn(30, 30)    n = len(colormaps)    fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3),                            constrained_layout=True, squeeze=False)    for [ax, cmap] in zip(axs.flat, colormaps):        psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)        fig.colorbar(psm, ax=ax)    plt.show()

  最简单的情况,我们可以键入一个颜色名称列表,以根据这些名称创建颜色映射。

· 

· 

cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])plot_examples([cmap])

 

  事实上,该列表可能包含任何有效的Matplotlib颜色规格(Matplotlib color specification)。Nx4 numpy数组对于创建自定义颜色映射特别有用。因为我们可以在这样的数组上进行各种各样的numpy操作,所以从现有的颜色映射中提取新的颜色映射变得非常简单。  例如,假设出于某种原因,我们想使256长度的“viridis”颜色图的前25个条目为粉红色:

· 

· 

· 

· 

· 

· 

· 

viridis = mpl.colormaps['viridis'].resampled(256)newcolors = viridis(np.linspace(0, 1, 256))pink = np.array([248/256, 24/256, 148/256, 1])newcolors[:25, :] = pinknewcmp = ListedColormap(newcolors)
plot_examples([viridis, newcmp])

 

  我们可以缩小颜色映射的动态范围;这里我们选择颜色图的中间部分。然而,请注意,由于viridis是一个列表颜色映射,我们最终会得到128个离散值,而不是原始颜色映射中的256个值。此方法不会在颜色空间中进行插值以添加新的颜色。

· 

· 

· 

viridis_big = mpl.colormaps['viridis']newcmp = ListedColormap(viridis_big(np.linspace(0.25, 0.75, 128)))plot_examples([viridis, newcmp])

  我们也可以很容易地连接两个颜色映射:

· 

· 

· 

· 

· 

· 

· 

top = mpl.colormaps['Oranges_r'].resampled(128)bottom = mpl.colormaps['Blues'].resampled(128)
newcolors = np.vstack((top(np.linspace(0, 1, 128)),                       bottom(np.linspace(0, 1, 128))))newcmp = ListedColormap(newcolors, name='OrangeBlue')plot_examples([viridis, newcmp])

  当然,我们不需要从命名的颜色映射开始,我们只需要创建Nx4数组来传递给ListedColormap。在这里,我们创建了一个从棕色(RGB:90,40,40)到白色(RGB:255,255,255)的颜色映射。

· 

· 

· 

· 

· 

· 

· 

N = 256vals = np.ones((N, 4))vals[:, 0] = np.linspace(90/256, 1, N)vals[:, 1] = np.linspace(40/256, 1, N)vals[:, 2] = np.linspace(40/256, 1, N)newcmp = ListedColormap(vals)plot_examples([viridis, newcmp])


创建线性分段颜色映射  LinearSegmentedColormap类使用锚点规定颜色映射,在锚点之间插RGB(A)值。  规定这些颜色映射的格式中,允许锚点处出现不连续。每个锚点的形式被规定为[x[i] yleft[i] yright[i]]的矩阵中的一行,其中x[i]是锚点,yleft[i]和yright[i]是锚定点左右两侧的颜色值。  如果没有间断,则yleft[i]==yright[i]:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

cdict = {'red':   [[0.0,  0.0, 0.0],                   [0.5,  1.0, 1.0],                   [1.0,  1.0, 1.0]],         'green': [[0.0,  0.0, 0.0],                   [0.25, 0.0, 0.0],                   [0.75, 1.0, 1.0],                   [1.0,  1.0, 1.0]],         'blue':  [[0.0,  0.0, 0.0],                   [0.5,  0.0, 0.0],                   [1.0,  1.0, 1.0]]}

def plot_linearmap(cdict):    newcmp = LinearSegmentedColormap('testCmap', segmentdata=cdict, N=256)    rgba = newcmp(np.linspace(0, 1, 256))    fig, ax = plt.subplots(figsize=(4, 3), constrained_layout=True)    col = ['r', 'g', 'b']    for xx in [0.25, 0.5, 0.75]:        ax.axvline(xx, color='0.7', linestyle='--')    for i in range(3):        ax.plot(np.arange(256)/256, rgba[:, i], color=col[i])    ax.set_xlabel('index')    ax.set_ylabel('RGB')    plt.show()
plot_linearmap(cdict)

 

  为了在锚点处形成不连续性,第三列与第二列不同。“红色”、“绿色”、“蓝色”和可选的“透明度”的矩阵设置为:

· 

· 

· 

· 

cdict['red'] = [...                [x[i]      yleft[i]     yright[i]],                [x[i+1]    yleft[i+1]   yright[i+1]],               ...]

  对于传递到x[i]和x[i+1]之间的颜色映射的值,插值在yright[i]和yleft[i+1]间。  在下面的例子中,0.5处有一个红色的不连续。0和0.5之间的插值方法是从0.3渐变到1,而0.5和1之间则从0.9渐变到1。请注意,red[0, 1]和red[2, 2]对于插值来说都是多余的,因为red[0, 1](即yleft[0])是0左边的值,而red[2, 2](即yright[2])是1右边的值,它们在颜色映射域之外。

· 

· 

· 

· 

cdict['red'] = [[0.0,  0.0, 0.3],                [0.5,  1.0, 0.9],                [1.0,  1.0, 1.0]]plot_linearmap(cdict)

 


直接用列表创建分段颜色映射  上面描述的方法非常通用,但无可否认,实现起来有点麻烦。对于某些简单使用情景,使用LinearSegmentedColormap.from_list可能更容易。它采用提供的颜色列表来创建具有相等间距的分段颜色映射。

· 

· 

colors = ["darkorange", "gold", "lawngreen", "lightseagreen"]cmap1 = LinearSegmentedColormap.from_list("mycmap", colors)

  如果需要,可以将颜色映射的节点指定为0到1之间的数字。例如,可以让红色部分在颜色映射中占据更多空间。

· 

· 

· 

· 

nodes = [0.0, 0.4, 0.8, 1.0]cmap2 = LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))
plot_examples([cmap1, cmap2])

 


反转颜色映射  Colormap.reversed创建一个新的颜色映射,该颜色映射是原始颜色映射的反转版本。

· 

· 

· 

· 

· 

· 

colors = ["#ffffcc", "#a1dab4", "#41b6c4", "#2c7fb8", "#253494"]my_cmap = ListedColormap(colors, name="my_cmap")
my_cmap_r = my_cmap.reversed()
plot_examples([my_cmap, my_cmap_r])

 

  如果没有传入名称,.reversed还会通过在原始颜色映射的名称后面附加“_r”来命名副本。
注册颜色映射  颜色映射可以添加到命名颜色映射matplotlib.colormaps列表中。这允许在绘图时按名称访问颜色映射:

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

· 

# my_cmap, my_cmap_r from reversing a colormapmpl.colormaps.register(cmap=my_cmap)mpl.colormaps.register(cmap=my_cmap_r)
data = [[1, 2, 3, 4, 5]]
fig, (ax1, ax2) = plt.subplots(nrows=2)
ax1.imshow(data, cmap='my_cmap')ax2.imshow(data, cmap='my_cmap_r')
plt.show()

 

参考  以下函数、方法、类和模块的使用如本例所示:matplotlib.axes.Axes.pcolormeshhttps://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.pcolormesh.html#matplotlib.axes.Axes.pcolormeshmatplotlib.figure.Figure.colorbarhttps://matplotlib.org/stable/api/figure_api.html#matplotlib.figure.Figure.colorbarmatplotlib.colorshttps://matplotlib.org/stable/api/colors_api.html#module-matplotlib.colorsmatplotlib.colors.LinearSegmentedColormaphttps://matplotlib.org/stable/api/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormapmatplotlib.colors.ListedColormaphttps://matplotlib.org/stable/api/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormapmatplotlib.cmhttps://matplotlib.org/stable/api/cm_api.html#module-matplotlib.cm

matplotlib.colormaps

https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.colormaps
本文链接matplotlib.colormaps:

https://matplotlib.org/stable/api/matplotlib_configuration_api.html#matplotlib.colormaps

ListedColormap:

https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.ListedColormap.html#matplotlib.colors.ListedColormap

LinearSegmentedColormap:

https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap

Choosing Colormaps in Matplotlib:

https://matplotlib.org/stable/tutorials/colors/colormaps.html

Colormap.resampled:

https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Colormap.resampled

LinearSegmentedColormap.from_list:

https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.LinearSegmentedColormap.html#matplotlib.colors.LinearSegmentedColormap.from_list

Colormap.reversed:

https://matplotlib.org/stable/api/_as_gen/matplotlib.colors.Colormap.html#matplotlib.colors.Color