color-mapping

color-mapping in matplotlib

color-mapping需要两个信息,对应两个步骤,

  1. 首先需要确定如何将要展示的数据映射到\((0\sim 1)\)区间,最基本的映射关系就是线性映射,将数据的最大值映射到1,最小值映射到0. 在matplotlib的绘图中,不同绘图方法的API都是一致的,这个关系通过norm参数指定,这个参数接收一个matplotlib.colors.Normalize对象,根据Normalize对象种类或属性的不同,我们可以指定不同的映射关系,比如线性映射或者对数映射.

  2. 其次需要确定如何把\((0\sim 1)\)区间的值再映射到RGBA空间,即颜色空间,这个信息我们称之为色图 (colormap),这个参数通过cmap参数指定. 这个参数接收一个字符串或者一个matplotlib.colors.Colormap对象. 对于matplotlib自带的colormap,用字符串指定cmap就可以了,比如cmap='jet'. 当然一些库可能提供了其他的colormap,这时候可以提供对应的Colormap对象. 任何绘图工具都会提供很多不同种类的colormap可供选择,下面是matplotlib中提供的一部分colormap,jet看起来比较适合描述温度场或者产热.

不同绘图工具的设计逻辑应该都是类似的. 有了这两个信息说明,我们就可以完全确定该如何进行color-mapping了. 以绘制某一个物理场的分布为例,在绘图过程中有两个地方需要进行color-mapping,一个是在绘制物理场分布的时候,需要将物理场的数值大小和颜色对应起来;另一个是在绘制colorbar的时候,我们也需要同样的映射信息.

关于plt.colorbar(mappable, ax)需要再说明一下,它主要需要两个参数,一个是mappable参数,也是第一个默认参数,它接收一个plt.cm.ScalarMappable(norm, cmap)对象,这个对象有两个属性normcmap,和上面提到的是一样的. 我们可以手动创建一个ScalarMappable对象传给colorbar,也可以把绘制了等值线的Axes对象传给它,因为绘制了等值线的Axes对象有同样的接口,比如norm或者cmap,此时colorbar也可以成功检测到对应属性并且完成绘制. ax参数决定了这个colorbar要绘制到哪一个Axes对象上.

一个小例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.colors as colors

plt.style.use('science')

def remove_spines(ax):
for spine in ["right", "top", "left", "bottom"]:
ax.spines[spine].set_visible(False)


def remove_ticklines(ax):
ax.tick_params(axis=u"both", which=u"both", length=0)


x = np.linspace(-1, 1, 100)
y = np.linspace(-1, 1, 100)
X, Y = np.meshgrid(x, y)
Z = X ** 2 + Y ** 2

fig, ax = plt.subplots(1, 2, figsize=(6, 3))
cmap = "jet"

# Linear color mapping
norm_linear = colors.Normalize(vmin=Z.min(), vmax=Z.max())
contourf_linear = ax[0].contourf(X, Y, Z, norm=norm_linear, cmap=cmap)

scalarmappable_linear = plt.cm.ScalarMappable(norm=norm_linear, cmap=cmap)
cbar_linear = plt.colorbar(mappable=scalarmappable_linear, ax=ax[0])

cbar_linear.set_ticks([0.01, 1, 2])
cbar_linear.set_ticklabels(["0.01", "1", "2"])
cbar_linear.ax.set_title(r"$q_{\text{gen}}\, (\mathrm{W/cm^3})$", y=1.05, x=4)
remove_ticklines(cbar_linear.ax)

ax[0].axis("square")

remove_spines(ax[0])
remove_ticklines(ax[0])

ax[0].set_xticks([-1, 0, 1])
ax[0].set_yticks([-1, 0, 1])

# Logarithmic color mapping
norm_log = colors.LogNorm(vmin=0.01, vmax=Z.max())
contourf_log = ax[1].pcolor(
X, Y, Z, norm=norm_log, cmap=cmap, linewidth=0, rasterized=True
)
cbar_log = plt.colorbar(mappable=contourf_log, ax=ax[1])
cbar_log.set_ticks([0.01, 1, 2])
cbar_log.set_ticklabels(["0.01", "1", "2"])
cbar_log.ax.set_title(r"$q_{\text{gen}}\, (\mathrm{W/cm^3})$", y=1.05, x=4)
remove_ticklines(cbar_log.ax)

ax[1].axis("square")
remove_spines(ax[1])
remove_ticklines(ax[1])
ax[1].set_xticks([-1, 0, 1])
ax[1].set_yticks([-1, 0, 1])

plt.savefig("fig1.png")

一些说明

  • plt.style.use('science')制定了默认绘图风格,science样式文件可以通过安装SciencePlots库获得. science风格默认用Latex渲染,如果未配置好Latex,上面代码中cbar_linear.ax.set_title里面的Latex命令需要删除..否则无法正常运行.

  • remove_spinesremove_ticklines两个函数接收一个Axes对象,分别删去它的Spines和Major+Minor ticks,这里这样指定是为了说明colorbar对象中也包含一个Axes对象,因此要修改colorbar对象的某些属性,实际上是修改colorbar对象中Axes对象的属性,这些接口都是一致的. 可以参考一下以前写的Artist对象.

  • Linear color mapping中说明了一下使用plt.cm.ScalarMappable传递给plt.colorbar作参数的方法,如果直接传给plt.colorbar(contourf_linear),这时候绘制的colorbar是离散的,因为plt.contourf(levels=5)得到的等值线图是离散的,离散出来的色块数量由levels参数决定,不是很好看.

  • Logarithmic color mapping中说明了plt.pcolor绘制物理场分布以及color-mapping对数映射的方法,这个方法实际上并不是绘制等值线,而是用类似于sns.heatmap这种像素式的热图,这种方法绘制出来的分布更加漂亮一些,看起来更高级一点.