设置颜色条¶
图形中的点、线、面构成色彩的连续标签,用颜色条最好。在Matplotlib中,颜色条是一个独立的坐标轴,可以指明图形中颜色的含义 。
import matplotlib.pyplot as plt
plt.style.use('classic')
%matplotlib inline
import numpy as np
从前面的例子可以看到,使用plt.colorbar 函数可以创建最简单的颜色条。
x = np.linspace(0, 10, 1000)
I = np.sin(x) * np.cos(x[:, np.newaxis])
plt.imshow(I)
plt.colorbar();
接下来,我们将探讨一些关于自定义这些色条并在不同场景中有效使用它们的方法。
自定义色条¶
可通过创建可视化图表的绘图函数中的 cmap 参数来指定颜色映rsn:
plt.imshow(I, cmap='gray');
所有可用的颜色映射均位于“plt.cm”命名空间中;使用 IPython 的制表符自动补全功能可获取完整的内置颜色映射列表:
plt.cm.<TAB>
然而,能够选择一种颜色映射图仅是第一步;更为关键的是如何在各种可能性中做出决策! 事实证明,这个选择比你最初想象的要微pect.
选择颜色表¶
关于可视化中颜色选择的全面论述超出了本书的范围;若想了解该主题及其他相关内容的精彩内容,可参阅文章《提升图表效果的十条简单法则》。Matplotlib的在线文档中也对色阶选择进行了深入探讨。
总体而言,您应了解三种不同的颜色映射类型:
顺序色阶图:由连续的颜色序列构成(例如二值色阶或绿色渐 变色阶)。 发散型色阶图:此类图表通常包含两种不同的颜色,分别表示相对于均值的正向和负向偏差(例如RdBu或 PuOr)。 定性色阶图:此类色阶图混合颜色时无特定顺序(例如彩虹色或喷 气式飞机色)。 喷气式色标图(jet colormap)在Matplotlib 2.0版本之前是默认使用的色标类型,属于定性色标的一个典型代表。然而,将其设为默认色标实属遗憾,因为定性色标通常并不适合用于呈现定量数据。其主要问题在于:随着色标范围的扩大,定性色标通常不会呈现出亮度的均匀渐变效果。
将喷流色条转换为黑白图像后,我们即and white:
from matplotlib.colors import LinearSegmentedColormap
def grayscale_cmap(cmap):
"""Return a grayscale version of the given colormap"""
cmap = plt.cm.get_cmap(cmap)
colors = cmap(np.arange(cmap.N))
# convert RGBA to perceived grayscale luminance
# cf. 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):
"""Plot a colormap with its grayscale equivalent"""
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')
请注意灰度图像中的明亮条纹。即使在全彩色显示下,这种亮度不均匀的现象也会使视觉注意力集中在色彩范围的某些区域,从而可能凸显数据集中不重要的部分。建议使用像 viridis(Matplotlib 2.0 的默认色阶)这样的色阶图——该色阶图专门设计为在整个色域内保持均匀的亮度变化。因此,它不仅与人类的颜色感知效果契合,也适用于灰度打印场景。
view_colormap('viridis')
如果您偏好彩虹色方案,对于连续数据而言,另一种不错的选择是“立方螺旋”(cubehelix)色阶图:
view_colormap('cubehelix')
对于其他情况(例如展示相对于某个均值的正负偏差),双色色条(如“RdBu”(红色-蓝色))可能非常实用。然而,如以下图所示,需要注意的是:在转换为灰度图像时,正负偏差的信息将会丢失!
view_colormap('RdBu')
在后续内容中,我们将看到如何运用这些颜色映射的具体示例。
Matplotlib提供了大量可用的颜色映射;要查看其列表,可使用IPython 浏览“plt.c m”子模块。若想采用更系统化的方法处理Python中的颜色问题,可参考Seaborn库中的工具与文档(详用 Seabo可视化r])。
颜色限制与扩展¶
Matplotlib提供了丰富的色条自定义功能。色条本身实际上是plt Axes的实例,因此我们之前学到的所有坐标轴和刻度格式设置技巧均可直接应用
。色条具有相当灵活的设置选项:例如,通过设置 extend 属性,可以缩小颜色范围,并在顶部和底部用三角形箭头标示超出范围的数值
。这一功能在显示存在噪声的图像时尤为实用。:
# make noise in 1% of the image pixels
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.cm.get_cmap() 函数,并传递合适的颜色映射名称及所需分箱数量:
plt.imshow(I, cmap=plt.cm.get_cmap('Blues', 6))
plt.colorbar()
plt.clim(-1, 1);
离散版本的色阶图可以像其他任何色阶图一样使用。
示例:手写数字¶
为了说明这一方法的实用性,让我们来看一个关于手写数字数据的有趣可视化示例。 该数据集包含在Scikit-Learn中,由近2000张8×8尺寸的缩略图组成,展示了多种手写数字。
目前,我们先下载数字数据,并使用 pltimshow() 对若干示例图像进行可视化展``:
# 加载数字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=[])
由于每个数字均由其64个像素的色调定义,我们可以将每个数字视为位于64维空间中的一个点:每个维度代表一个像素的亮度。 然而,在如此高维空间中可视化关系可能极其困难 。 一种可行的方法是采用降维技术(如流形学习),在保留目标关系的同时降低数据维 度。 降维是无监督机器学习的一个典型应用案例,我什么学习]一文中对此进行更详细的探讨。
暂不讨论这些细节,我们先来看看这些数字数据的二维流ynb)):
# 使用 IsoMap 将数字投影到二维空间
from sklearn.manifold import Isomap
iso = Isomap(n_components=2)
projection = iso.fit_transform(digits.data)
我们将使用离散色阶图来展示结果,并调整刻度线和色调设置,以提升最终色条的视觉美感:
# 绘制结果
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)
该投影图还为我们揭示了数据集内部关系的一些有趣见解:例如,在此投影中,数字5和3的分布范围几乎重叠,表明某些手写数字5和3难以区分,因此更易被自动化分类算法混淆;而其他数值(如0和1)的分布范围则更为分散,因此被混淆的可能性要小得多。这一观察结果与我们的直觉一致——因为数字5和3的外观相似度远高于数字0和1。