Python之Matplotlib数据可视化(三):配置颜色条
- 一个简易的颜色条图例
- 一个采用灰度配色方案的图形
- 选择配色方案
- 顺序配色方案
- 互逆配色方案
- 定性配色方案
- jet 配色方案与非等差的渐变亮度
- viridis 配色方案和渐变亮度 scale
- cubehelix 配色方案和渐变亮度
- RdBu 配色方案和渐变亮度
- 颜色条刻度的限制与扩展功能的设置
- 设置颜色条扩展属性
- 离散型颜色条
- 案例:手写数字
- 手写数字样本
- 手写数字像素的流形学习结果
- 备注
图例通过离散的标签表示离散的图形元素。然而,对于图形中由彩色的点、线、面构成的连续标签,用颜色条来表示的效果比较好。在 Matplotlib 里面,颜色条是一个独立的坐标轴,可以指明图形中颜色的含义。
首先导入需要使用的画图工具:
1 2 3 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np |
一个简易的颜色条图例
通过 plt.colorbar 函数就可以创建最简单的颜色条:
1 2 3 4 | x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) plt.imshow(I) plt.colorbar(); |
1 2 3 4 5 6 7 8 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) plt.imshow(I) plt.colorbar(); plt.show() |
下面将介绍一些颜色条的个性化配置方法,让你能将它们有效地应用于不同场景中。
一个采用灰度配色方案的图形
可以通过 cmap 参数为图形设置颜色条的配色方案
1 | plt.imshow(I, cmap='gray'); |
1 2 3 4 5 6 7 8 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) plt.imshow(I, cmap='gray'); plt.colorbar(); plt.show() |
所有可用的配色方案都在 plt.cm 命名空间里面,在 IPython 里通过 Tab 键就可以查看所有的配置方案:
1 | plt.cm.<TAB> |
有了这么多能够作为备选的配色方案只是第一步,更重要的是如何确定用哪种方案!最终的选择结果可能和你一开始想用的有很大不同。
选择配色方案
可以参考文章“Ten Simple Rules for Better Figures”。
Matplotlib 的在线文档中也有关于配色方案选择的有趣论述(http://matplotlib.org/1.4.1/users/colormaps.html)。
一般情况下,你只需要重点关注三种不同的配色方案:
顺序配色方案
由一组连续的颜色构成的配色方案(例如
互逆配色方案
通常由两种互补的颜色构成,表示正反两种含义(例如
定性配色方案
随机顺序的一组颜色(例如
jet 配色方案与非等差的渐变亮度
可以通过把 jet 转换为黑白的灰度图看看具体的颜色:
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 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np from matplotlib.colors import LinearSegmentedColormap def grayscale_cmap(cmap): """为配色方案显示灰度图""" cmap = plt.cm.get_cmap(cmap) colors = cmap(np.arange(cmap.N)) # 将RGBA色转换为不同亮度的灰度值 # 参考链接http://alienryderflex.com/hsp.html RGB_weight = [0.299, 0.587, 0.114] luminance = np.sqrt(np.dot(colors[:, :3] ** 2, RGB_weight)) colors[:, :3] = luminance[:, np.newaxis] return LinearSegmentedColormap.from_list(cmap.name + "_gray", colors, cmap.N) def view_colormap(cmap): """用等价的灰度图表示配色方案""" cmap = plt.cm.get_cmap(cmap) colors = cmap(np.arange(cmap.N)) cmap = grayscale_cmap(cmap) grayscale = cmap(np.arange(cmap.N)) fig, ax = plt.subplots(2, figsize=(6, 2), subplot_kw=dict(xticks=[], yticks=[])) ax[0].imshow([colors], extent=[0, 10, 0, 1]) ax[1].imshow([grayscale], extent=[0, 10, 0, 1]) view_colormap('jet') plt.show() |
注意观察灰度图里比较亮的那部分条纹。这些亮度变化不均匀的条纹在彩色图中对应某一段彩色区间,由于色彩太接近容易突显出数据集中不重要的部分,导致眼睛无法识别重点。
viridis 配色方案和渐变亮度 scale
更好的配色方案是 viridis (已经成为 Matplotlib 2.0 的默认配色方案)。它采用了精心设计的亮度渐变方式,这样不仅便于视觉观察,而且转换成灰度图后也更清晰:
1 | view_colormap('viridis') |
cubehelix 配色方案和渐变亮度
如果你喜欢彩虹效果,可以用 cubehelix 配色方案来可视化连续的数值
1 | view_colormap('cubehelix') |
RdBu 配色方案和渐变亮度
至于其他的场景,例如要用两种颜色表示正反两种含义时,可以使用 RdBu 双色配色方案(红色 - 蓝色,Red-Blue 简称)。但正如图 所示,用红色、蓝色表示的正反两种信息在灰度图上看不出差别!
1 | view_colormap('RdBu') |
Matplotlib 里面有许多配色方案,在 IPython 里面用 Tab 键浏览 plt.cm 模块就可以看到所有内容。关于 Python 语言中配色的更多基本原则,可以参考 Seaborn 程序库的工具和文档。
颜色条刻度的限制与扩展功能的设置
设置颜色条扩展属性
Matplotlib 提供了丰富的颜色条配置功能。由于可以将颜色条本身仅看作是一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) # 为图形像素设置1%噪点 speckles = (np.random.random(I.shape) < 0.01) I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles)) plt.figure(figsize=(10, 3.5)) plt.subplot(1, 2, 1) plt.imshow(I, cmap='RdBu') plt.colorbar() plt.subplot(1, 2, 2) plt.imshow(I, cmap='RdBu') plt.colorbar(extend='both') plt.clim(-1, 1); plt.show() |
左边那幅图是用默认的颜色条刻度限制实现的效果,噪点的范围完全覆盖了我们感兴趣的数据。而右边的图形设置了颜色条的刻度上下限,并在上下限之外增加了扩展功能,这样的数据可视化图形显然更有效果。
离散型颜色条
虽然颜色条默认都是连续的,但有时你可能也需要表示离散数据。最简单的做法就是使用
1 2 3 4 5 6 7 8 9 10 11 12 | import matplotlib.pyplot as plt plt.style.use('classic') import numpy as np x = np.linspace(0, 10, 1000) I = np.sin(x) * np.cos(x[:, np.newaxis]) # 为图形像素设置1%噪点 speckles = (np.random.random(I.shape) < 0.01) I[speckles] = np.random.normal(0, 3, np.count_nonzero(speckles)) plt.imshow(I, cmap=plt.cm.get_cmap('Blues', 6)) plt.colorbar() plt.clim(-1, 1); plt.show() |
这种离散型颜色条和其他颜色条的用法相同。
案例:手写数字
让我们来看一些有趣的手写数字可视化图,这可能是一个比较实用的案例。数据在
手写数字样本
先下载数据,然后用
1 2 3 4 5 6 7 8 9 10 | import matplotlib.pyplot as plt plt.style.use('classic') # 加载数字0~5的图形,对其进行可视化 from sklearn.datasets import load_digits digits = load_digits(n_class=6) fig, ax = plt.subplots(8, 8, figsize=(6, 6)) for i, axi in enumerate(ax.flat): axi.imshow(digits.images[i], cmap='binary') axi.set(xticks=[], yticks=[]) plt.show() |
手写数字像素的流形学习结果
由于每个数字都由 64 像素的色相(hue)构成,因此可以将每个数字看成是一个位于 64维空间的点,即每个维度表示一个像素的亮度。但是想通过可视化来描述如此高维度的空间是非常困难的。一种解决方案是通过降维技术,在尽量保留数据内部重要关联性的同时降低数据的维度,例如流形学习(manifold learning)。降维是无监督学习的重要内容。
先来看看如何用流形学习将这些数据投影到二维空间进行可视化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import matplotlib.pyplot as plt plt.style.use('classic') # 加载数字0~5的图形,对其进行可视化 from sklearn.datasets import load_digits digits = load_digits(n_class=6) # 用IsoMap方法将数字投影到二维空间 from sklearn.manifold import Isomap iso = Isomap(n_components=2) projection = iso.fit_transform(digits.data) # 画图用离散型颜色条来显示结果,调整 ticks 与 clim 参数来改善颜色条 plt.scatter(projection[:, 0], projection[:, 1], lw=0.1, c=digits.target, cmap=plt.cm.get_cmap('cubehelix', 6)) plt.colorbar(ticks=range(6), label='digit value') plt.clim(-0.5, 5.5) plt.show() |
这个投影结果还向我们展示了一些数据集的有趣特性。例如,数字 5 与数字 3 在投影中有大面积重叠,说明一些手写的 5 与 3 难以区分,因此自动分类算法也更容易搞混它们。其他的数字,像数字 0 与数字 1,隔得特别远,说明两者不太可能出现混淆。这个观察结果也符合我们的直观感受,因为 5 和 3 看起来确实要比 0 和 1 更像。
备注
各位老铁来个“关注”、“点赞”、“评论”三连击哦
各位老铁来个“关注”、“点赞”、“评论”三连击哦
各位老铁来个“关注”、“点赞”、“评论”三连击哦