Matplotlib进阶教程:颜色讲解

56763a35faba05b38e96e4932a5df1e1.gif

后台回复【看书】

即可获取python相关电子书~

Hi,我是山月。

今天来给大家介绍下Matplotlib里的颜色部分。

01

指定颜色

Matplotlib可以通过以下格式来指定颜色:

  • 一个RGB或RGBA(红,绿,蓝,alpha)元组,元组内元素是在封闭区间[0,1]内的浮点数(如:(0.1,0.2,0.5)或(0.1,0.2,0.5,0.3));
  • 十六进制的 RGB 或 RGBA 字符串(如:“#0f0f0f”或“#0f0f0f80”,不区分大小写);
  • 一个简写的十六进制 RGB 或 RGBA 字符串,把每个字符复制即可获得完整的十六进制 RGB 或 RGBA 字符串,(如:“#abc”相当于“#aabbcc”;“#abcd”相当于“#aabbccdd”;不区分大小写);
  • 封闭区间[0,1]内的浮点数字符串,包括灰度(如’0.5′);
  • {‘b’, ‘g’, ‘r’, ‘c’, ‘m’, ‘y’, ‘k’, ‘w’} 之一,它们是蓝色、绿色、红色、青色、品红、黄色、黑色和白色的单个字符速记符号。
  • X11/CSS4 颜色名称(不区分大小写);
  • xkcd 颜色名称,前缀为“xkcd:”(例如,“xkcd:sky blue”;不区分大小写)。
  • ‘T10’ 分类调色板中的一种 Tableau 颜色(默认颜色循环):{‘tab:blue’, ‘tab:orange’, ‘tab:green’, ‘tab:red’, ‘tab:purple’, ‘tab:brown’, ‘tab:pink’, ‘tab:gray’, ‘tab:olive’, ‘tab:cyan’}(不区分大小写);
  • “CN”颜色规范,即’C’后面跟着一个数字,它是默认属性循环的索引(matplotlib.rcParams[‘ axis .prop_cycle’]);索引将在呈现时发生,如果循环不包含颜色,则默认为黑色。

“Red”, “Green”和”Blue”是这些颜色的强度,它们的组合跨越了色彩空间。

“Alpha”的行为方式取决于 Artist 的 zorder,高zorder Artists 绘制在低 Artists 之上,“Alpha”决定低Artists是否被高Artists覆盖。

如果一个像素的旧 RGB 是 RGBold,而要添加的 Artist 像素的 RGB 是带有alpha 的 RGBnew。

则该像素的 RGB 更新为:RGB = RGBOld * (1 – Alpha) + RGBnew * Alpha。

Alpha 为 1 表示旧颜色完全被新 Artist 覆盖,Alpha 为 0 表示 Artist 的像素是透明的。

1、“CN”颜色选择

一旦创建artist,“CN”颜色就会转换为 RGBA。如:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib as mpl

th = np.linspace(0, 2*np.pi, 128)

def demo(sty):
    mpl.style.use(sty)
    fig, ax = plt.subplots(figsize=(3, 3))

    ax.set_title('style: {!r}'.format(sty), color='C0')

    ax.plot(th, np.cos(th), 'C1', label='C1')
    ax.plot(th, np.sin(th), 'C2', label='C2')
    ax.legend()

demo('default')
demo('seaborn')

plt.show()

Effect:

ca4810c527e31c72ad6da756882d5cd6.png

使用第一种颜色作为标题,然后使用第二种和第三种颜色绘制每种样式。

2、xkcd 和 X11/CSS4

xkcd 颜色名称详情可看:https://xkcd.com/color/rgb/;

X11/CSS4 和 xkcd 之间有 95 个名称冲突,其中只有3个名称对应的十六进制值是相同的。

如,’blue’映射到’#0000FF’,而’xkcd:blue’映射到’#0343DF’。

也正是由于这些名称冲突存在,所以所有的xkcd颜色名称都有前缀’xkcd:’。

名称冲突如下图所示,其中十六进制值一致的颜色名称以粗体显示:

import matplotlib.pyplot as plt
import matplotlib._color_data as mcd
import matplotlib.patches as mpatch

overlap = {name for name in mcd.CSS4_COLORS
           if "xkcd:" + name in mcd.XKCD_COLORS}

fig = plt.figure(figsize=[4.8, 16])
ax = fig.add_axes([0, 0, 1, 1])

for j, n in enumerate(sorted(overlap, reverse=True)):
    weight = None
    cn = mcd.CSS4_COLORS[n]
    xkcd = mcd.XKCD_COLORS["xkcd:" + n].upper()
    if cn == xkcd:
        weight = 'bold'

    r1 = mpatch.Rectangle((0, j), 1, 1, color=cn)
    r2 = mpatch.Rectangle((1, j), 1, 1, color=xkcd)
    txt = ax.text(2, j+.5, '  ' + n, va='center', fontsize=10,
                  weight=weight)
    ax.add_patch(r1)
    ax.add_patch(r2)
    ax.axhline(j, color='k')

ax.text(.5, j + 1.5, 'X11', ha='center', va='center')
ax.text(1.5, j + 1.5, 'xkcd', ha='center', va='center')
ax.set_xlim(0, 3)
ax.set_ylim(0, j + 2)
ax.axis('off')

plt.show()

Effect:

1cadd8ef1bf0e59b43047b52acbca7b5.png

ps:为什么说有95个名称冲突,但是只有49个颜色名称?

因为除了3个一样的十六进制之外,其它46个名称都对应了2个十六进制,所以46*2+3=95。

02

自定义颜色条

颜色条需要一个“可映射”(matplotlib.cm.ScalarMappable)对象(通常是图像)来指示要使用的色图和标准。

如果想创建没有附加图像的颜色条,可以改为使用没有关联数据的 ScalarMappable。

1、基本连续颜色条

现在让我们创建一个带有刻度和标签的基本连续颜色条。

colorbar调用的参数包括ScalarMapable(使用norm和cmap参数构造)、绘制colorbar的axes以及colorbar的方向。

import matplotlib.pyplot as plt
import matplotlib as mpl

fig, ax = plt.subplots(figsize=(6, 1))
fig.subplots_adjust(bottom=0.5)

cmap = mpl.cm.cool
norm = mpl.colors.Normalize(vmin=5, vmax=10)

fig.colorbar(mpl.cm.ScalarMappable(norm=norm, cmap=cmap),
             cax=ax, orientation='horizontal', label='Some Units')

plt.show()

Effect:

d4e7427ab143b53c9b1f668c12765c0a.png

2、离散间隔颜色条

第二个示例演示了如何使用ListedColormap,它从一组列出的颜色生成一个色图。

colors.BoundaryNorm根据离散间隔和扩展端生成色图索引,以显示“over”和“under”值颜色。

Over 和 under 用于显示标准化[0,1]范围之外的数据。在这里,我们将颜色作为灰色阴影传递给编码 0-1 范围内浮点数的字符串。

如果使用 ListedColormap,则 bounds 数组的长度必须比颜色列表的长度大 1。边界必须是单调递增的。

这一次,除了之前的参数外,我们还要向colorbar传递更多的参数。

要在colorbar上显示超出范围的值,我们必须使用extend关键字参数。要使用extend,必须指定两个额外的边界。

最后,间距参数确保间隔按比例显示在colorbar上。

import matplotlib.pyplot as plt
import matplotlib as mpl

fig, ax = plt.subplots(figsize=(6, 1))
fig.subplots_adjust(bottom=0.5)

cmap = mpl.colors.ListedColormap(['red', 'green', 'blue', 'cyan'])
cmap.set_over('0.25')
cmap.set_under('0.75')

bounds = [1, 2, 4, 7, 8]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
    mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
    cax=ax,
    boundaries=[0] + bounds + [13],
    extend='both',
    ticks=bounds,
    spacing='proportional',
    orientation='horizontal',
    label='Discrete intervals, some other units',
)

plt.show()

Effect:

28c0166feac8addb094b6723e3a2d8de.png

3、具有自定义扩展长度的彩条

在这里,我们说明了对具有离散间隔的颜色条使用自定义长度颜色条扩展。

要使每个扩展的长度与内部颜色的长度相同,得使用 extendfrac=’auto’。

import matplotlib.pyplot as plt
import matplotlib as mpl

fig, ax = plt.subplots(figsize=(6, 1))
fig.subplots_adjust(bottom=0.5)

cmap = mpl.colors.ListedColormap(['royalblue', 'cyan',
                                  'yellow', 'orange'])
cmap.set_over('red')
cmap.set_under('blue')

bounds = [-1.0, -0.5, 0.0, 0.5, 1.0]
norm = mpl.colors.BoundaryNorm(bounds, cmap.N)
fig.colorbar(
    mpl.cm.ScalarMappable(cmap=cmap, norm=norm),
    cax=ax,
    boundaries=[-10] + bounds + [10],
    extend='both',
    extendfrac='auto',
    ticks=bounds,
    spacing='uniform',
    orientation='horizontal',
    label='Custom extension lengths, some other units',
)

plt.show()

Effect:

7018c93056bc9b7cb9eb90eb3463c026.png

03

创建颜色图

Matplotlib 有许多可通过 matplotlib.cm.get_cmap 访问的内置色图。还有一些外部库,如 palettable,它们有许多额外的色图。

然而,我们经常想在 Matplotlib 中创建或操作色图,这可以使用 ListedColormap 或 LinearSegmentedColormap 类来完成。

从外部看,两个色图类都将 0 到 1 之间的值映射到一堆颜色。但是,有一些细微的差别,其中一些如下所示。

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

1、获取色图并访问它们的值

首先,获取一个已命名的色图,其中大部分在 Matplotlib 中的可选色图中列出,可以使用 matplotlib.cm.get_cmap 完成,它会返回一个色图对象。

第二个参数给出了用于定义色图的颜色列表的大小,我们使用一个适度的值 8:

from matplotlib import cm

viridis = cm.get_cmap('viridis', 8)
print(viridis)  # <matplotlib.colors.ListedColormap object at 0x0000024BACC8AFD0>

对象 viridis 是一个可调用对象,当传递一个介于 0 和 1 之间的浮点数时,它会从色图中返回一个 RGBA 值:

print(viridis(0.56))    # (0.122312, 0.633153, 0.530398, 1.0)

1)ListedColormap

ListedColormap 将它们的颜色值存储在 .colors 属性中,因此可以使用 colors 属性直接访问组成色图的颜色列表。

也可以通过调用viridis来间接访问它,该viridis的值数组与colormap的长度相匹配。

请注意,返回的列表是 RGBA Nx4 数组的形式,其中 N 是色图的长度。

import numpy as np
from matplotlib import cm

viridis = cm.get_cmap('viridis', 8)

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.      ]]
'''

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

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.      ]]
'''

2)LinearSegmentedColormap

LinearSegmentedColormap 没有 .colors 属性。但是,仍然可以使用整数数组或 0 到 1 之间的浮点数组调用色图。

import numpy as np
from matplotlib import cm

copper = cm.get_cmap('copper', 8)
print(copper)    # <matplotlib.colors.LinearSegmentedColormap object at 0x0000020C937BD668>

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.        ]]
'''

2、创建列出的色图

创建色图得本质是上面的逆操作,我们向 ListedColormap 提供颜色规范的列表或数组以创建新的色图。

在这里我们会定义一个辅助函数:plot_examples,该函数将多个色图之一作为输入,创建一些随机数据,并将色图应用于该数据集的图像图。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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])

Effect:

a8350622051b6fb1c0efd70cbf2cde9c.png

事实上,该列表可能包含任何有效的 matplotlib 颜色规范。

Nx4 numpy 数组对创建自定义色图特别有用,因为我们可以在这样的数组上执行各种 numpy 操作,因此从现有色图制作新色图变得非常简单。

比如我们想要将 256 长度的“viridis”色图得前 25 个条目设为粉红色:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib import cm

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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()

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)

plot_examples([viridis, newcmp])

Effect:

2cbc031904293f11999826d35264fa6d.png

我们可以轻松地减小色图的动态范围;比如我们选择色图的中值0.5。

但是,我们需要从更大的颜色图进行插值,否则新的颜色图将具有重复值。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib import cm

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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()

viridis = cm.get_cmap('viridis', 256)
viridisBig = cm.get_cmap('viridis', 512)
newcmp = ListedColormap(viridisBig(np.linspace(0.25, 0.75, 256)))
plot_examples([viridis, newcmp])

Effect:

379d122196e04b4af65a8b1060c2938f.png

我们可以很容易地连接两个颜色图:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib import cm

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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()

viridis = cm.get_cmap('viridis', 256)
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])

Effect:

c9c13a92326e55264db9200f92c62601.png

当我们不需要从一个已命名的色图开始时,我们只需要创建 Nx4 数组传递给 ListedColormap。

创建一个从棕色 (RGB: 90,40,40) 到白色 (RGB: 255,255,255) 的色图。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import ListedColormap
from matplotlib import cm

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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()

viridis = cm.get_cmap('viridis', 256)

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

Effect:

b4cd9a5c1439f95f4537ffa10e3a7cdf.png

3、创建线性分段色图

LinearSegmentedColormap使用在RGB(A)值之间插值的锚点来指定色图。

这些颜色图以允许在锚点处不连续的格式指定。

每个锚点被指定为 [x[i] yleft[i] yright[i]] 形式的矩阵中的一行,其中 x[i] 是锚点,yleft[i] 和 yright[i] 是锚点两侧的颜色值。

如果没有不连续点,则 yleft[i]=yright[i]:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

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)

Effect:

4fab5f9043fbe42ba07a2adb33c3d352.png

为了在锚点处产生不连续性,第三列与第二列不同。“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] 是 0 左边的值,red[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)

Effect:

7027c48dbb6690da1d4432d14d3438ec.png

1)直接从列表创建分段色图

以上是一种非常通用的方法,但实施起来确实有点麻烦。

对于一些基本情况,使用 LinearSegmentedColormap.from_list 可能更容易。

它从提供的颜色列表中创建具有相等间距的分段颜色图。

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

如果需要,可以将色图的节点指定为 0 到 1 之间的数字。

例如,带红色的部分可能会占用颜色图中的更多空间。

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import LinearSegmentedColormap

def plot_examples(colormaps):
    """
    辅助函数,可以用关联的色图绘制数据
    """
    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()

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

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

plot_examples([cmap1, cmap2])

Effect:

703b86d2188105ab91cd33c681d00a3e.png

04

颜色图归一化

默认情况下,可以使用色图的对象将色图中的颜色从数据值vmin线性映射到vmax。如:

pcm = ax.pcolormesh(x, y, Z, vmin=-1., vmax=1., cmap='RdBu_r')

将 Z 中的数据从 -1 线性映射到 +1,因此 Z=0 将在色图 RdBu_r 的中心给出颜色(在这种情况下为白色)。

Matplotlib 会分两步进行此映射,首先将输入数据标准化为 [0, 1],然后映射到色图中的索引。

规范化是在 matplotlib.colors() 模块中定义的类,默认的线性归一化是 matplotlib.colors.Normalize()。

将数据映射到颜色的Artists传递参数 vmin 和 vmax 来构造一个 matplotlib.colors.Normalize() 实例,然后调用它:

import matplotlib as mpl

norm = mpl.colors.Normalize(vmin=-1.,vmax=1.)
print(norm(0.)) # 0.5

但是,有时以非线性方式将数据映射到颜色图很有用。

1、对数

最常见的转换之一是通过取其对数(以 10 为底)来绘制数据,这种转换对于显示不同比例的变化很有用。

可以使用 colors.LogNorm 通过 log10 来将数据规范化。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]

Z1 = np.exp(-(X)**2 - (Y)**2)
Z2 = np.exp(-(X * 10)**2 - (Y * 10)**2)
Z = Z1 + 50 * Z2

fig, ax = plt.subplots(2, 1)

pcm = ax[0].pcolor(X, Y, Z,
                   norm=colors.LogNorm(vmin=Z.min(), vmax=Z.max()),
                   cmap='PuBu_r')
fig.colorbar(pcm, ax=ax[0], extend='max')

pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r')
fig.colorbar(pcm, ax=ax[1], extend='max')
plt.show()

Effect:

24f1f93d3e1df26b9646ba3ba9b26221.png

2、对称对数

虽然有时会出现正数和负数,但我们仍然希望将对数刻度应用于两者。在这种情况下,负数也按对数缩放,但映射到较小的数字;

例如,如果 vmin=-vmax,那么它们的负数从 0 映射到 0.5,正数从 0.5 映射到 1。

由于接近零的值的对数趋于无穷大,因此需要线性映射零附近的小范围。

参数 linthresh 允许用户指定此范围的大小(-linthresh,linthresh)。色图中此范围的大小由 linscale(默认1.0) 设置。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

fig, ax = plt.subplots(2, 1)

pcm = ax[0].pcolormesh(X, Y, Z,
                       norm=colors.SymLogNorm(linthresh=0.03, linscale=0.03,
                                              vmin=-1.0, vmax=1.0, base=10),
                       cmap='RdBu_r')
fig.colorbar(pcm, ax=ax[0], extend='both')

pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z))
fig.colorbar(pcm, ax=ax[1], extend='both')
plt.show()

Effect:

13f37f88ccce119952f3cb0079fe2276.png

3、幂律

有时将颜色重新映射到幂律关系(即y=xγ,其中 γ 是幂)是有用的。为此,我们可以使用colors.PowerNorm。

它会将 gamma 作为参数(gamma == 1.0 只会产生默认的线性归一化):

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

N = 100
X, Y = np.mgrid[0:3:complex(0, N), 0:2:complex(0, N)]
Z1 = (1 + np.sin(Y * 10.)) * X**(2.)

fig, ax = plt.subplots(2, 1)

pcm = ax[0].pcolormesh(X, Y, Z1, norm=colors.PowerNorm(gamma=0.5),
                       cmap='PuBu_r')
fig.colorbar(pcm, ax=ax[0], extend='max')

pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r')
fig.colorbar(pcm, ax=ax[1], extend='max')
plt.show()

Effect:

8052112d38b63afa0b3c3bebd0d9a396.png

4、离散边界

Matplotlib 附带的另一个规范化是 colors.BoundaryNorm。

除了 vmin 和 vmax 之外,它还需要映射数据的边界作为参数。然后颜色在这些边界之间线性分布。如:

import numpy as np
import matplotlib.colors as colors

bounds = np.array([-0.25, -0.125, 0, 0.5, 1])
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=4)
print(norm([-0.2,-0.15,-0.02, 0.3, 0.8, 0.99])) # [0 0 1 2 3 3]

注意:与其他规范不同,此规范返回从 0 到 ncolors-1 的值。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

N = 100
X, Y = np.mgrid[-3:3:complex(0, N), -2:2:complex(0, N)]
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (Z1 - Z2) * 2

fig, ax = plt.subplots(3, 1, figsize=(8, 8))
ax = ax.flatten()
# 均匀边界会产生类似于轮廓的效果
bounds = np.linspace(-1, 1, 10)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax[0].pcolormesh(X, Y, Z,
                       norm=norm,
                       cmap='RdBu_r')
fig.colorbar(pcm, ax=ax[0], extend='both', orientation='vertical')

# 不均匀边界改变了颜色映射:
bounds = np.array([-0.25, -0.125, 0, 0.5, 1])
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax[1].pcolormesh(X, Y, Z, norm=norm, cmap='RdBu_r')
fig.colorbar(pcm, ax=ax[1], extend='both', orientation='vertical')

pcm = ax[2].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z))
fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical')
plt.show()

Effect:

1bacfec4e61302f1f267048f997a8411.png

5、TwoSlopeNorm

有时我们希望在中心点的两侧有不同的颜色图,并且我们希望这两个颜色图具有不同的线性比例。

我们以一张地形图为例:

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cbook as cbook

filename = cbook.get_sample_data('topobathy.npz', asfileobj=False)
with np.load(filename) as dem:
    topo = dem['topo']
    longitude = dem['longitude']
    latitude = dem['latitude']

fig, ax = plt.subplots()
# 制作一张彩色地图,清晰地描绘陆地和海洋,长度相同(256+256)
colors_undersea = plt.cm.terrain(np.linspace(0, 0.17, 256))
colors_land = plt.cm.terrain(np.linspace(0.25, 1, 256))
all_colors = np.vstack((colors_undersea, colors_land))
terrain_map = colors.LinearSegmentedColormap.from_list('terrain_map',
    all_colors)

# 制定标准:注意中心偏移,以便土地具有更大的动态范围:
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
    cmap=terrain_map,)
# 简单的地理绘图,设置纵横比,因为经线之间的距离取决于纬度。
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
fig.colorbar(pcm, shrink=0.6)
plt.show()

Effect:

cd4507b1a217c0ecdf8d2ccb398c53cc.png

6、自定义规范

上面描述的 TwoSlopeNorm 为自定义规范提供了一个有用的示例。

import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors
import matplotlib.cbook as cbook

filename = cbook.get_sample_data('topobathy.npz', asfileobj=False)
with np.load(filename) as dem:
    topo = dem['topo']
    longitude = dem['longitude']
    latitude = dem['latitude']

fig, ax = plt.subplots()
colors_undersea = plt.cm.terrain(np.linspace(0, 0.17, 256))
colors_land = plt.cm.terrain(np.linspace(0.25, 1, 256))
all_colors = np.vstack((colors_undersea, colors_land))
terrain_map = colors.LinearSegmentedColormap.from_list('terrain_map',
    all_colors)

class MidpointNormalize(colors.Normalize):
    def __init__(self, vmin=None, vmax=None, vcenter=None, clip=False):
        self.vcenter = vcenter
        colors.Normalize.__init__(self, vmin, vmax, clip)

    def __call__(self, value, clip=None):
        x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1]
        return np.ma.masked_array(np.interp(value, x, y))
        
midnorm = MidpointNormalize(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=midnorm,
    cmap=terrain_map)
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
fig.colorbar(pcm, shrink=0.6, extend='both')
plt.show()

Effect:

dedc2ec543e9d55ded644827cc84a40b.png

05

选择颜色图

Matplotlib 有许多可通过 matplotlib.cm.get_cmap 访问的内置色图,还有像 [palettable] 和 [colorcet] 这样的外部库,它们有许多额外的色图。

在这里,我们简要讨论如何在众多选项之间进行选择。

1、概述

选择一个好色图是因为想在 3D 颜色空间中为数据集找到一个好的表示。

任何给定数据集的最佳颜色图取决于许多因素,包括:

  • 是表示形式数据还是度量数据([Ware])
  • 数据集的知识(例如,是否存在其他值偏离的临界值?)
  • 绘制的参数是否有直观的配色方案
  • 在这个领域是否有观众可能期望的标准

对于许多应用程序,感知一致的颜色图是最佳选择——数据中的相等步长被视为颜色空间中的相等步长。

研究人员发现,当数据发生变化时,人脑对亮度参数变化的感知比色调等变化要好得多。因此,具有单调增加亮度的颜色图将更好地被观看者解释。

[colorcet]是一个很好的感知统一色图的例子。

颜色可以以各种方式在 3D 空间中表示,一种表示颜色的方法是使用 CIELAB。

在 CIELAB 中,色彩空间可以通过亮度(L∗);red-green(a*);和yellow-blue(b∗)表示。

然后可以使用参数亮度来了解更多关于 matplotlib 色图将如何被观众感知的信息。

2、色图类别

颜色图通常根据功能分为几类:

  • Sequential: 颜色的亮度和饱和度逐渐变化,通常使用单一色调;应该用于表示有顺序的信息。
  • Diverging: 在不饱和色中遇到的两种不同颜色的亮度和可能的饱和度的变化,当所绘制的信息具有一个关键中值(例如地形或数据偏离零附近时)时可以使用。
  • Cyclic: 两种不同颜色在中间相交并以不饱和颜色开始/结束的亮度变化;应该用于在端点处环绕的值,例如相位角、风向或一天中的时间。
  • Qualitative: 常为杂色;应该用来表示没有顺序或关系的信息。
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from matplotlib import cm
from colorspacious import cspace_converter # 提前用pip安装好 colorspacious
from collections import OrderedDict

cmaps = OrderedDict()

1)Sequential

对于Sequential图,亮度值通过色图单调增加。

色图中的一些 L∗ 值从 0 到 100(二进制和其他灰度),而其他一些从L∗=20 开始。L∗ 范围较小的那些将相应地具有较小的感知范围。

还要注意 L∗ 函数在色图中有所不同:一些在L∗ 中近似线性,而另一些则更弯曲。

cmaps['Perceptually Uniform Sequential'] = [
            'viridis', 'plasma', 'inferno', 'magma', 'cividis']

cmaps['Sequential'] = [
            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']

显示每个颜色图的范围:

import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict

cmaps = OrderedDict()

cmaps['Perceptually Uniform Sequential'] = [
            'viridis', 'plasma', 'inferno', 'magma', 'cividis']
'''
# 换成这句得到第二张图
cmaps['Sequential'] = [
            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']
'''
nrows = max(len(cmap_list) for cmap_category, cmap_list in cmaps.items())
gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))


def plot_color_gradients(cmap_category, cmap_list, nrows):
    fig, axes = plt.subplots(nrows=nrows)
    fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99)
    axes[0].set_title(cmap_category + ' colormaps', fontsize=14)

    for ax, name in zip(axes, cmap_list):
        ax.imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
        pos = list(ax.get_position().bounds)
        x_text = pos[0] - 0.01
        y_text = pos[1] + pos[3]/2.
        fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)

    for ax in axes:
        ax.set_axis_off()

for cmap_category, cmap_list in cmaps.items():
    plot_color_gradients(cmap_category, cmap_list, nrows)

plt.show()

Effect:

55c8af24a6b593aecfbf7a83a282461e.png

524d74d12eead4eb97f34ea2b79db3e3.png

2)Sequential2

Sequential2 图中的许多L∗ 值是单调递增的,但有些在 L∗ 空间中会出现平稳甚至上下波动。

其他(afmhot、copper、gist_heat 和 hot)在L∗ 函数中有扭结。在色图的平稳或扭结区域中表示的数据将导致对数据的带状感知。

cmaps['Sequential (2)'] = [
            'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
            'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
            'hot', 'afmhot', 'gist_heat', 'copper']

色图范围:

228c8df7947699acdc2000d42913e8e9.png

3)Diverging

对于Diverging图,我们希望L∗ 值单调递增到最大值,该值应接近L∗=100,然后单调递减 L∗值。我们会在色图的两端寻找近似相等的最小L∗ 值。

按这些指标衡量,BrBG 和 RdBu 是不错的选择。虽然coolwarm 也是一个不错的选择,但它不涵盖广泛的 L∗ 值。

cmaps['Diverging'] = [
            'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
            'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']

色图范围:

9bb5aae52bd449c4dc8d5a2761c44d89.png

4)Cyclic

对于Cyclic图,我们希望以相同的颜色开始和结束,并在中间遇到一个对称的中心点。

常用的 HSV 色图包含在这组色图中,尽管它与中心点不对称。

cmaps['Cyclic'] = ['twilight', 'twilight_shifted', 'hsv']

色图范围:

cb27b91ba5cca8f4ee4970df192b231d.png

5)Qualitative

L∗ 值会在整个色图中到处移动,并且不是单调递增的。

cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                        'Dark2', 'Set1', 'Set2', 'Set3',
                        'tab10', 'tab20', 'tab20b', 'tab20c']

色图范围:

49f030fc7ea2a5aa34498170be74a591.png

6)Miscellaneous

Miscellaneous色图具有创建它们的特定用途。

cmaps['Miscellaneous'] = [
            'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
            'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
            'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar']

色图范围:

d3150c846555a86d8053290128eb5c5d.png

3、色图亮度

matplotlib 色图的亮度值:

import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
from matplotlib import cm
from colorspacious import cspace_converter
import matplotlib as mpl

cmaps = OrderedDict()

cmaps['Perceptually Uniform Sequential'] = [
            'viridis', 'plasma', 'inferno', 'magma', 'cividis']

cmaps['Sequential'] = [
            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']

cmaps['Sequential (2)'] = [
            'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
            'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
            'hot', 'afmhot', 'gist_heat', 'copper']

cmaps['Diverging'] = [
            'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
            'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']

cmaps['Cyclic'] = ['twilight', 'twilight_shifted', 'hsv']

cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                        'Dark2', 'Set1', 'Set2', 'Set3',
                        'tab10', 'tab20', 'tab20b', 'tab20c']

cmaps['Miscellaneous'] = [
            'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
            'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
            'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar']


mpl.rcParams.update({'font.size': 12})

_DSUBS = {'Perceptually Uniform Sequential': 5, 'Sequential': 6,
          'Sequential (2)': 6, 'Diverging': 6, 'Cyclic': 3,
          'Qualitative': 4, 'Miscellaneous': 6}

# 色图之间的间距
_DC = {'Perceptually Uniform Sequential': 1.4, 'Sequential': 0.7,
       'Sequential (2)': 1.4, 'Diverging': 1.4, 'Cyclic': 1.4,
       'Qualitative': 1.4, 'Miscellaneous': 1.4}

# 通过颜色映射进行分步索引
x = np.linspace(0.0, 1.0, 100)

# 绘制
for cmap_category, cmap_list in cmaps.items():

    #进行子图绘制,以便色图有足够的空间。默认值为每个子图上有6个色图。
    dsub = _DSUBS.get(cmap_category, 6)
    nsubplots = int(np.ceil(len(cmap_list) / dsub))

    # squeeze=False 以类似方式处理单个子批次的情况
    fig, axes = plt.subplots(nrows=nsubplots, squeeze=False,
                             figsize=(7, 2.6*nsubplots))

    for i, ax in enumerate(axes.flat):

        locs = []  # 文本标签的位置

        for j, cmap in enumerate(cmap_list[i*dsub:(i+1)*dsub]):

            # 获取colormap的RGB值,并在CAM02-UCS颜色空间中转换色图。lab[0, :, 0]是亮度
            rgb = cm.get_cmap(cmap)(x)[np.newaxis, :, :3]
            lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)

            # 为每个类别单独绘制色图的亮度值。
            if cmap_category == 'Sequential':
                y_ = lab[0, ::-1, 0]
                c_ = x[::-1]
            else:
                y_ = lab[0, :, 0]
                c_ = x

            dc = _DC.get(cmap_category, 1.4)  # cmaps 水平间距
            ax.scatter(x + j*dc, y_, c=c_, cmap=cmap, s=300, linewidths=0.0)

            # 存储色图标签位置
            if cmap_category in ('Perceptually Uniform Sequential',
                                 'Sequential'):
                locs.append(x[-1] + j*dc)
            elif cmap_category in ('Diverging', 'Qualitative', 'Cyclic',
                                   'Miscellaneous', 'Sequential (2)'):
                locs.append(x[int(x.size/2.)] + j*dc)

        # 设置轴的限制:
        #   * 第1个子图用作x轴极限的参考
        #   * 亮度值从0到100 (y轴限制)
        ax.set_xlim(axes[0, 0].get_xlim())
        ax.set_ylim(0.0, 100.0)

        # 为色图设置标签
        ax.xaxis.set_ticks_position('top')
        ticker = mpl.ticker.FixedLocator(locs)
        ax.xaxis.set_major_locator(ticker)
        formatter = mpl.ticker.FixedFormatter(cmap_list[i*dsub:(i+1)*dsub])
        ax.xaxis.set_major_formatter(formatter)
        ax.xaxis.set_tick_params(rotation=50)
        ax.set_ylabel('Lightness $L^*$', fontsize=12)

    ax.set_xlabel(cmap_category + ' colormaps', fontsize=14)

    fig.tight_layout(h_pad=0.0, pad=1.5)
    plt.show()

效果(一次只显示一个图形,删除后将按顺序显示其余图形):

8cb79eca0868d9a7823aac115ff50a41.png

855d15912daddc5ac4d06be26091db57.png

74959a63260f684448e5982989c73e68.png

eb61f988abcdee6d81133f63fa29b38d.png

492efe9c0eba1f71ed32734ab53545fd.png

b63ce447dc68a82aaada4298596b24e4.png

d5fb8a25b0d11783a50b4711725ba144.png

4、灰度转换

小心将颜色图转换为灰度图,因为它们可能会在黑白打印机上打印。

有许多不同的方法可以转换为灰度:

较好的方法使用像素rgb值的线性组合,但根据对颜色强度的感知进行加权。

转换为灰度的非线性方法是使用像素的L∗ 值。

import numpy as np
import matplotlib.pyplot as plt
from collections import OrderedDict
from matplotlib import cm
from colorspacious import cspace_converter
import matplotlib as mpl

cmaps = OrderedDict()

cmaps['Perceptually Uniform Sequential'] = [
            'viridis', 'plasma', 'inferno', 'magma', 'cividis']

cmaps['Sequential'] = [
            'Greys', 'Purples', 'Blues', 'Greens', 'Oranges', 'Reds',
            'YlOrBr', 'YlOrRd', 'OrRd', 'PuRd', 'RdPu', 'BuPu',
            'GnBu', 'PuBu', 'YlGnBu', 'PuBuGn', 'BuGn', 'YlGn']

cmaps['Sequential (2)'] = [
            'binary', 'gist_yarg', 'gist_gray', 'gray', 'bone', 'pink',
            'spring', 'summer', 'autumn', 'winter', 'cool', 'Wistia',
            'hot', 'afmhot', 'gist_heat', 'copper']

cmaps['Diverging'] = [
            'PiYG', 'PRGn', 'BrBG', 'PuOr', 'RdGy', 'RdBu',
            'RdYlBu', 'RdYlGn', 'Spectral', 'coolwarm', 'bwr', 'seismic']

cmaps['Cyclic'] = ['twilight', 'twilight_shifted', 'hsv']

cmaps['Qualitative'] = ['Pastel1', 'Pastel2', 'Paired', 'Accent',
                        'Dark2', 'Set1', 'Set2', 'Set3',
                        'tab10', 'tab20', 'tab20b', 'tab20c']

cmaps['Miscellaneous'] = [
            'flag', 'prism', 'ocean', 'gist_earth', 'terrain', 'gist_stern',
            'gnuplot', 'gnuplot2', 'CMRmap', 'cubehelix', 'brg',
            'gist_rainbow', 'rainbow', 'jet', 'nipy_spectral', 'gist_ncar']


mpl.rcParams.update({'font.size': 14})

x = np.linspace(0.0, 1.0, 100)

gradient = np.linspace(0, 1, 256)
gradient = np.vstack((gradient, gradient))


def plot_color_gradients(cmap_category, cmap_list):
    fig, axes = plt.subplots(nrows=len(cmap_list), ncols=2)
    fig.subplots_adjust(top=0.95, bottom=0.01, left=0.2, right=0.99,
                        wspace=0.05)
    fig.suptitle(cmap_category + ' colormaps', fontsize=14, y=1.0, x=0.6)

    for ax, name in zip(axes, cmap_list):

        # 获取子图的RGB值。
        rgb = cm.get_cmap(plt.get_cmap(name))(x)[np.newaxis, :, :3]

        # 在CAM02-UCS颜色空间中获取色图,我们需要的是亮度。
        lab = cspace_converter("sRGB1", "CAM02-UCS")(rgb)
        L = lab[0, :, 0]
        L = np.float32(np.vstack((L, L, L)))

        ax[0].imshow(gradient, aspect='auto', cmap=plt.get_cmap(name))
        ax[1].imshow(L, aspect='auto', cmap='binary_r', vmin=0., vmax=100.)
        pos = list(ax[0].get_position().bounds)
        x_text = pos[0] - 0.01
        y_text = pos[1] + pos[3]/2.
        fig.text(x_text, y_text, name, va='center', ha='right', fontsize=10)

    # 关闭图
    for ax in axes.flat:
        ax.set_axis_off()

    plt.show()


for cmap_category, cmap_list in cmaps.items():

    plot_color_gradients(cmap_category, cmap_list)

Effect:

cc99fb5282df43450a710fb4dbbdf1e6.png

8fd59ceb78e066241daef50e16a51804.png

01d0e2e0b0d42274e103ec6ad4af5435.png

696168f90fdf7fcebab3e7ee0caf8f8d.png

44b50ceff17f39c77a7394d6631930f9.png

9410e5fd84ecbfe7cf1cb69a219d5a2c.png

6b0a32139ca6dc74ed3fe17f3c0a4c79.png

好了,今天就到这里啦~

2122b86095dfafe70ed7d32c8f82475b.png

END

1bc1c1acc3e3b9a0e117170f6462924c.gif

您的“点赞”、“观看”、“分享”是我们输出的动力。

文章出处登录后可见!

已经登录?立即刷新

共计人评分,平均

到目前为止还没有投票!成为第一位评论此文章。

(0)
心中带点小风骚的头像心中带点小风骚普通用户
上一篇 2022年5月8日
下一篇 2022年5月8日

相关推荐