备注
Go to the end 下载完整的示例代码.
在 Matplotlib 中创建颜色映射#
Matplotlib 有许多内置的颜色映射,可以通过 matplotlib.colormaps 访问. 还有像 palettable 这样的外部库,它们有许多额外的颜色映射.
然而,我们也可能想要创建或操作我们自己的颜色映射.这可以使用类 ListedColormap 或 LinearSegmentedColormap 来完成.这两个颜色映射类都将 0 和 1 之间的值映射到颜色.但是,正如以下解释的那样,它们之间存在差异.
在手动创建或操作颜色映射之前,让我们首先看看如何从现有的颜色映射类中获取颜色映射及其颜色.
获取颜色映射并访问其值#
首先,可以使用 matplotlib.colormaps 获取命名的颜色映射,其中大多数在 在 Matplotlib 中选择颜色映射 中列出,它返回一个颜色映射对象.用于在内部定义颜色映射的颜色列表的长度可以通过 Colormap.resampled 进行调整.下面我们使用一个适中的值 8,这样就没有太多要查看的值.
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
from matplotlib.colors import LinearSegmentedColormap, ListedColormap
viridis = mpl.colormaps['viridis'].resampled(8)
对象 viridis 是一个可调用对象,当传递一个介于 0 和 1 之间的浮点数时,它会从颜色映射返回一个 RGBA 值:
print(viridis(0.56))
(np.float64(0.122312), np.float64(0.633153), np.float64(0.530398), np.float64(1.0))
ListedColormap#
ListedColormap 将其颜色值存储在 .colors 属性中.可以使用 colors 属性直接访问组成颜色映射的颜色列表,也可以通过使用与颜色映射长度匹配的值数组调用 viridis 来间接访问它. 请注意,返回的列表是 RGBA (N, 4) 数组的形式,其中 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#
LinearSegmentedColormap 没有 .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. ]]
创建 Listed Colormap#
创建颜色映射本质上是上述操作的逆运算,我们将颜色规范的列表或数组提供给 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),
layout='constrained', 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 color specification .对于创建自定义颜色映射特别有用的是 (N, 4) 形状的数组.因为我们可以对这种数组进行各种 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, :] = pink
newcmp = 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])

当然,我们不需要从命名的颜色映射开始,我们只需要创建 (N, 4) 数组以传递给 ListedColormap . 在这里,我们创建一个从棕色(RGB:90, 40, 40)到白色(RGB:255, 255, 255)的颜色映射.
N = 256
vals = 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), layout='constrained')
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)

为了在锚点处产生不连续性,第三列与第二列不同. "red","green","blue"以及可选的"alpha"的矩阵设置如下:
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 还会通过向原始颜色映射的名称 appending '_r' 来命名副本.
注册颜色映射#
可以将颜色映射添加到 matplotlib.colormaps 的命名颜色映射列表中.这允许在绘图函数中按名称访问颜色映射:
# my_cmap, my_cmap_r from reversing a colormap
mpl.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.pcolormeshmatplotlib.figure.Figure.colorbarmatplotlib.colorsmatplotlib.colors.LinearSegmentedColormapmatplotlib.colors.ListedColormapmatplotlib.cmmatplotlib.colormaps
脚本的总运行时间:(0 分钟 4.567 秒)