颜色图标准化#

默认情况下使用颜色图的对象将颜色图中的颜色从数据值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().

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

In [1]: import matplotlib as mpl

In [2]: norm = mpl.colors.Normalize(vmin=-1, vmax=1)

In [3]: norm(0)
Out[3]: 0.5

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

对数#

最常见的转换之一是通过取其对数(以 10 为底)来绘制数据。这种转换对于显示不同比例的变化很有用。使用通过colors.LogNorm规范化数据 \(log_{10}\). 在下面的示例中,有两个凸起,一个比另一个小得多。使用colors.LogNorm,可以清楚地看到每个凹凸的形状和位置:

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

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

# A low hump with a spike coming out of the top right.  Needs to have
# z/colour axis on a log scale so we see both hump and spike.  linear
# scale only shows the spike.
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', shading='auto')
fig.colorbar(pcm, ax=ax[0], extend='max')

pcm = ax[1].pcolor(X, Y, Z, cmap='PuBu_r', shading='auto')
fig.colorbar(pcm, ax=ax[1], extend='max')
plt.show()
颜色映射规范

居中#

在很多情况下,数据围绕中心对称,例如,围绕中心 0 的正负异常。在这种情况下,我们希望将中心映射到 0.5,并映射与中心偏差最大的数据点如果其值大于中心,则为 1.0,否则为 0.0。规范colors.CenteredNorm会自动创建这样的映射。它非常适合与使用不同颜色边缘的发散颜色图相结合,这些边缘在中心以不饱和颜色相遇。

如果对称中心不为 0,则可以使用 vcenter参数设置。中心两侧的对数刻度见下 colors.SymLogNorm图;要在中心上方和下方应用不同的映射,请使用colors.TwoSlopeNormbelow。

delta = 0.1
x = np.arange(-3.0, 4.001, delta)
y = np.arange(-4.0, 3.001, delta)
X, Y = np.meshgrid(x, y)
Z1 = np.exp(-X**2 - Y**2)
Z2 = np.exp(-(X - 1)**2 - (Y - 1)**2)
Z = (0.9*Z1 - 0.5*Z2) * 2

# select a divergent colormap
cmap = cm.coolwarm

fig, (ax1, ax2) = plt.subplots(ncols=2)
pc = ax1.pcolormesh(Z, cmap=cmap)
fig.colorbar(pc, ax=ax1)
ax1.set_title('Normalize()')

pc = ax2.pcolormesh(Z, norm=colors.CenteredNorm(), cmap=cmap)
fig.colorbar(pc, ax=ax2)
ax2.set_title('CenteredNorm()')

plt.show()
标准化(),CenteredNorm()

对称对数#

类似地,有时会出现正数据和负数据,但我们仍然希望将对数比例应用于两者。在这种情况下,负数也按对数缩放,并映射到更小的数字;例如,如果vmin=-vmax,则负数从 0 映射到 0.5,正数从 0.5 映射到 1。

由于接近零的值的对数趋于无穷大,因此需要线性映射零附近的小范围。参数 linthresh允许用户指定此范围的大小(- linthreshlinthresh)。颜色图中此范围的大小由linscale设置。当linscale == 1.0(默认值)时,用于线性范围正负半部分的空间将等于对数范围中的十进制。

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', shading='auto')
fig.colorbar(pcm, ax=ax[0], extend='both')

pcm = ax[1].pcolormesh(X, Y, Z, cmap='RdBu_r', vmin=-np.max(Z), shading='auto')
fig.colorbar(pcm, ax=ax[1], extend='both')
plt.show()
颜色映射规范

幂律#

有时将颜色重新映射到幂律关系(即\(y=x^{\gamma}\), 在哪里\(\gamma\)是权力)。为此,我们使用colors.PowerNorm. 它将 gamma 作为参数 gamma == 1.0 只会产生默认的线性归一化):

笔记

使用这种类型的转换来绘制数据可能应该有充分的理由。技术查看器习惯于线性和对数轴以及数据转换。幂律不太常见,应该明确地让观众知道它们已被使用。

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, constrained_layout=True)

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

pcm = ax[1].pcolormesh(X, Y, Z1, cmap='PuBu_r', shading='auto')
fig.colorbar(pcm, ax=ax[1], extend='max')
ax[1].set_title('Normalize()')
plt.show()
PowerNorm()、归一化()

离散边界#

Matplotlib 附带的另一个规范化是colors.BoundaryNorm. 除了vminvmax之外,这还需要映射数据的参数边界。然后颜色在这些“边界”之间线性分布。它还可以采用扩展参数将上限和/或下限超出范围值添加到颜色分布的范围。例如:

In [4]: import matplotlib.colors as colors

In [5]: bounds = np.array([-0.25, -0.125, 0, 0.5, 1])

In [6]: norm = colors.BoundaryNorm(boundaries=bounds, ncolors=4)

In [7]: print(norm([-0.2, -0.15, -0.02, 0.3, 0.8, 0.99]))
[0 0 1 2 3 3]

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

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

fig, ax = plt.subplots(2, 2, figsize=(8, 6), constrained_layout=True)
ax = ax.flatten()

# Default norm:
pcm = ax[0].pcolormesh(X, Y, Z, cmap='RdBu_r')
fig.colorbar(pcm, ax=ax[0], orientation='vertical')
ax[0].set_title('Default norm')

# Even bounds give a contour-like effect:
bounds = np.linspace(-1.5, 1.5, 7)
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')
ax[1].set_title('BoundaryNorm: 7 boundaries')

# Bounds may be unevenly spaced:
bounds = np.array([-0.2, -0.1, 0, 0.5, 1])
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256)
pcm = ax[2].pcolormesh(X, Y, Z, norm=norm, cmap='RdBu_r')
fig.colorbar(pcm, ax=ax[2], extend='both', orientation='vertical')
ax[2].set_title('BoundaryNorm: nonuniform')

# With out-of-bounds colors:
bounds = np.linspace(-1.5, 1.5, 7)
norm = colors.BoundaryNorm(boundaries=bounds, ncolors=256, extend='both')
pcm = ax[3].pcolormesh(X, Y, Z, norm=norm, cmap='RdBu_r')
# The colorbar inherits the "extend" argument from BoundaryNorm.
fig.colorbar(pcm, ax=ax[3], orientation='vertical')
ax[3].set_title('BoundaryNorm: extend="both"')
plt.show()
默认范数,BoundaryNorm:7 个边界,BoundaryNorm:非均匀,BoundaryNorm:extend=

TwoSlopeNorm:中心两侧的不同映射

有时我们希望在概念中心点的任一侧都有不同的颜色图,并且我们希望这两个颜色图具有不同的线性比例。一个示例是地形图,其中陆地和海洋的中心为零,但陆地的海拔范围通常大于水的深度范围,并且它们通常由不同的颜色图表示。

dem = cbook.get_sample_data('topobathy.npz', np_load=True)
topo = dem['topo']
longitude = dem['longitude']
latitude = dem['latitude']

fig, ax = plt.subplots()
# make a colormap that has land and ocean clearly delineated and of the
# same length (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)

# make the norm:  Note the center is offset so that the land has more
# dynamic range:
divnorm = colors.TwoSlopeNorm(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=divnorm,
                    cmap=terrain_map, shading='auto')
# Simple geographic plot, set aspect ratio because distance between lines of
# longitude depends on latitude.
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
ax.set_title('TwoSlopeNorm(x)')
cb = fig.colorbar(pcm, shrink=0.6)
cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000])
plt.show()
双斜率范数(x)

FuncNorm: 任意函数归一化#

如果上述规范没有提供您想要的规范化,您可以使用 FuncNorm自己定义。请注意,此示例PowerNorm与 0.5 的幂相同:

def _forward(x):
    return np.sqrt(x)


def _inverse(x):
    return x**2

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

norm = colors.FuncNorm((_forward, _inverse), vmin=0, vmax=20)
pcm = ax.pcolormesh(X, Y, Z1, norm=norm, cmap='PuBu_r', shading='auto')
ax.set_title('FuncNorm(x)')
fig.colorbar(pcm, shrink=0.6)
plt.show()
函数范数(x)

自定义归一化:手动实现两个线性范围#

上述TwoSlopeNorm内容为定义您自己的规范提供了一个有用的示例。请注意,要使颜色条正常工作,您必须为您的规范定义一个逆:

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

    def __call__(self, value, clip=None):
        # I'm ignoring masked values and all kinds of edge cases to make a
        # simple example...
        # Note also that we must extrapolate beyond vmin/vmax
        x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1.]
        return np.ma.masked_array(np.interp(value, x, y,
                                            left=-np.inf, right=np.inf))

    def inverse(self, value):
        y, x = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1]
        return np.interp(value, x, y, left=-np.inf, right=np.inf)


fig, ax = plt.subplots()
midnorm = MidpointNormalize(vmin=-500., vcenter=0, vmax=4000)

pcm = ax.pcolormesh(longitude, latitude, topo, rasterized=True, norm=midnorm,
                    cmap=terrain_map, shading='auto')
ax.set_aspect(1 / np.cos(np.deg2rad(49)))
ax.set_title('Custom norm')
cb = fig.colorbar(pcm, shrink=0.6, extend='both')
cb.set_ticks([-500, 0, 1000, 2000, 3000, 4000])

plt.show()
自定义规范

脚本总运行时间:(0分5.849秒)

由 Sphinx-Gallery 生成的画廊