Matplotlib is hiring a Research Software Engineering Fellow! See discourse for details. Apply by January 3, 2020
Matplotlib有许多内置的颜色映射,可通过 matplotlib.cm.get_cmap
. 还有一些外部库,比如 palettable 有很多额外的彩色地图。
但是,我们经常希望在Matplotlib中创建或操作颜色映射。这可以用类来完成 ListedColormap
以及一个介于0和1之间的nx4 nummy值数组,以表示颜色映射的rgba值。还有一个 LinearSegmentedColormap
类,该类允许使用一些定义段的定位点指定颜色映射,并在定位点之间进行线性插值。
首先,获取一个命名的颜色映射,其中大部分列在 在Matplotlib中选择颜色映射 需要使用 matplotlib.cm.get_cmap
,返回 matplotlib.colors.ListedColormap
对象。第二个参数给出了用于定义颜色映射的颜色列表的大小,下面我们使用一个适度的值12,因此没有太多的值可供查看。
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
from collections import OrderedDict
viridis = cm.get_cmap('viridis', 12)
print(viridis)
出:
<matplotlib.colors.ListedColormap object at 0x7f127f31bba8>
客体 viridis
是可调用的,当传递介于0和1之间的浮点值时,将从颜色映射返回rgba值:
print(viridis(0.56))
出:
(0.119512, 0.607464, 0.540218, 1.0)
组成颜色映射的颜色列表可以使用 colors
属性,或者可以通过调用 viridis
与颜色映射的长度匹配的值数组。请注意,返回的列表以rgba nx4数组的形式出现,其中n是颜色映射的长度。
print('viridis.colors', viridis.colors)
print('viridis(range(12))', viridis(range(12)))
print('viridis(np.linspace(0, 1, 12))', viridis(np.linspace(0, 1, 12)))
出:
viridis.colors [[0.267004 0.004874 0.329415 1. ]
[0.283072 0.130895 0.449241 1. ]
[0.262138 0.242286 0.520837 1. ]
[0.220057 0.343307 0.549413 1. ]
[0.177423 0.437527 0.557565 1. ]
[0.143343 0.522773 0.556295 1. ]
[0.119512 0.607464 0.540218 1. ]
[0.166383 0.690856 0.496502 1. ]
[0.319809 0.770914 0.411152 1. ]
[0.525776 0.833491 0.288127 1. ]
[0.762373 0.876424 0.137064 1. ]
[0.993248 0.906157 0.143936 1. ]]
viridis(range(12)) [[0.267004 0.004874 0.329415 1. ]
[0.283072 0.130895 0.449241 1. ]
[0.262138 0.242286 0.520837 1. ]
[0.220057 0.343307 0.549413 1. ]
[0.177423 0.437527 0.557565 1. ]
[0.143343 0.522773 0.556295 1. ]
[0.119512 0.607464 0.540218 1. ]
[0.166383 0.690856 0.496502 1. ]
[0.319809 0.770914 0.411152 1. ]
[0.525776 0.833491 0.288127 1. ]
[0.762373 0.876424 0.137064 1. ]
[0.993248 0.906157 0.143936 1. ]]
viridis(np.linspace(0, 1, 12)) [[0.267004 0.004874 0.329415 1. ]
[0.283072 0.130895 0.449241 1. ]
[0.262138 0.242286 0.520837 1. ]
[0.220057 0.343307 0.549413 1. ]
[0.177423 0.437527 0.557565 1. ]
[0.143343 0.522773 0.556295 1. ]
[0.119512 0.607464 0.540218 1. ]
[0.166383 0.690856 0.496502 1. ]
[0.319809 0.770914 0.411152 1. ]
[0.525776 0.833491 0.288127 1. ]
[0.762373 0.876424 0.137064 1. ]
[0.993248 0.906157 0.143936 1. ]]
颜色映射是一个查找表,因此“过采样”颜色映射将返回最近的邻居插值(请注意下面列表中重复的颜色)
print('viridis(np.linspace(0, 1, 15))', viridis(np.linspace(0, 1, 15)))
出:
viridis(np.linspace(0, 1, 15)) [[0.267004 0.004874 0.329415 1. ]
[0.267004 0.004874 0.329415 1. ]
[0.283072 0.130895 0.449241 1. ]
[0.262138 0.242286 0.520837 1. ]
[0.220057 0.343307 0.549413 1. ]
[0.177423 0.437527 0.557565 1. ]
[0.143343 0.522773 0.556295 1. ]
[0.119512 0.607464 0.540218 1. ]
[0.119512 0.607464 0.540218 1. ]
[0.166383 0.690856 0.496502 1. ]
[0.319809 0.770914 0.411152 1. ]
[0.525776 0.833491 0.288127 1. ]
[0.762373 0.876424 0.137064 1. ]
[0.993248 0.906157 0.143936 1. ]
[0.993248 0.906157 0.143936 1. ]]
这对于上面的逆向操作很重要,我们提供一个nx4 numpy数组,所有值都在0到1之间,to ListedColormap
制作新的颜色图。这意味着,我们可以对nx4数组执行的任何numpy操作都会使现有颜色映射中的新颜色映射的木工工作非常直接。
假设我们出于某种原因要将256长度的“Viridis”彩色地图的前25个条目设为粉红色:
viridis = cm.get_cmap('viridis', 256)
newcolors = viridis(np.linspace(0, 1, 256))
pink = np.array([248/256, 24/256, 148/256, 1])
newcolors[:25, :] = pink
newcmp = ListedColormap(newcolors)
def plot_examples(cms):
"""
helper function to plot two colormaps
"""
np.random.seed(19680801)
data = np.random.randn(30, 30)
fig, axs = plt.subplots(1, 2, figsize=(6, 3), constrained_layout=True)
for [ax, cmap] in zip(axs, cms):
psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
fig.colorbar(psm, ax=ax)
plt.show()
plot_examples([viridis, newcmp])
我们可以很容易地减小颜色映射的动态范围;这里我们选择颜色映射的中间0.5。但是,我们需要从较大的颜色映射中插入,否则新的颜色映射将具有重复的值。
viridisBig = cm.get_cmap('viridis', 512)
newcmp = ListedColormap(viridisBig(np.linspace(0.25, 0.75, 256)))
plot_examples([viridis, newcmp])
我们可以很容易地连接两个颜色映射:
top = cm.get_cmap('Oranges_r', 128)
bottom = cm.get_cmap('Blues', 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
. 在这里,我们创建了一个棕色的颜色映射,它变为白色……
N = 256
vals = np.ones((N, 4))
vals[:, 0] = np.linspace(90/256, 1, N)
vals[:, 1] = np.linspace(39/256, 1, N)
vals[:, 2] = np.linspace(41/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。注意红色 [0, 1] 和红色 [2, 2] 都是多余的插值,因为红色 [0, 1] 是0左边的值,红色 [2, 2] 是1.0右边的值。
cdict['red'] = [[0.0, 0.0, 0.3],
[0.5, 1.0, 0.9],
[1.0, 1.0, 1.0]]
plot_linearmap(cdict)