seaborn color palettes

Seaborn便于使用与数据特征及可视化目标相符的配色方案。这一章不仅讨论选择配色的基本原则,也介绍了seaborn中快速选择最佳方案的工具

绘图中使用颜色的基本原则

颜色的组成

根据眼睛的工作原理,每种颜色都可以用三个成分定义出来。我们通常指定RGB值来编码颜色,即红色、绿色、蓝色通道的强度。然而,为了分析颜色的感知属性,最好从色调 (hue)、饱和度 (saturation)、亮度 (luminance)的角度去考虑

色调使用非技术手段区分不同颜色的,它定义了第一级名字,比如红和绿:

饱和度是指色度,饱和度不同时,两个不同色调的颜色更容易区分:

亮度是指有多少光发射出来了 (或者反射出来),范围是从黑到白:

使用不同色调区分类别

如果想在图中绘制多个类别的数据,通常会改变元素的颜色加以区分。想想这个简单的例子:下面两个图哪个更容易统计三角形的数量:

在右图中,橙色的三角形非常显眼,很容易跟圆形进行区分。这种凸显的效果是因为我们的视觉系统优先捕捉颜色的不同

蓝色和橙色主要是色调不同。色调在区分类别上非常有用:大多数人能够轻易地分辨一定数量的色调,不同色调的点拥有相似的亮度也同样重要。来看一个例子:

大多数人能很快发现左图中有五个不同的类别,并且能够找出其中蓝色的点

右图中所有点都是蓝色的,只是亮度和饱和度不同,这就很难看出到底有几种类别。我们该怎么讨论其中的某个类别呢?有点蓝但又不是特别蓝的点?另外,灰色的点仿佛要与背景融为一体,看起来不如比较蓝的点突出。如果每个类别都一样重要,这种配色方案就不太好

所以,使用不同色调区分类别是一条通用的原则。以下是一些注意事项:如果图中颜色很多,很难记住哪个颜色对应哪个类别,除非在类别和颜色之间有已知的相互关系。这就使得图片难以解读:不仅要关注数据,还要不断地看图例找对应关系。所以尽量不要把图画的太复杂。另外要注意,不是每个人对颜色的感知都完全一样。同时使用不同形状 (或者其它属性)与不同颜色可以帮助色觉障碍的人理解图片,也可以使图片在黑白打印时也一样方便解读

使用不同亮度代表数值

然而,色调变化不太适合展示数值型数据。例如,在双变量直方图中用颜色代表计数。左图使用圆形调色板,每个观测的数值变化对应色调的变化。右图使用的调色板用更亮的颜色代表更大的数值:

使用基于色调的调色板,很难发现双变量分布的形状。相反,亮度调色板清晰地展示了两个主要的峰

改变亮度有助于发现数据的结构,亮度的变化更容易直观地理解为重要性的变化。但右图并没有使用灰度调色板。它的色度使其更有趣,微妙的色调变化使人感觉两个值的差别更大。这样就更容易分辨微小的差异

这些例子表明调色板的选择并非只涉及审美:如果颜色选得好,就能有效揭示数据中的模式;如果选得不好则会掩盖数据中的模式。没有最好的配色方案,但是对于特定数据集和可视化方法来说,配色确实有优劣之分

审美也很重要:越多人愿意看你的图,他们就越有可能从图里发现些什么。你在自己画图自己看时也是这样。在探索性数据分析的过程中,你可能会生成大量类似的图片。改变配色方案能带来新鲜感,让你能够投入地从数据中发现有趣的特征

选择调色板的工具

调整调色板最重要的函数是color_palette()。大部分seaborn中生成调色板的方法都可以使用这个函数提供的界面。所有带有palette参数的函数内部都是使用这个函数

color_palette()的基本参数通常是个字符串:可以是某个调色板的名字,也可以是一个家族的名字及选择特定颜色的其它参数。在后面的例子中,color_palette()会代理cubehelix_palette()等更具体的函数。也可以传入一个颜色列表,任何matplotlib能够接受的形式都可以 (RGB元组、十六进制代码、X11表中的名字)。函数返回的对象封装了一个RGB元组的列表和一些有用的方法,比如转换成十六进制代码及HTML代码等

不使用任何参数调用color_palette()会返回当前默认的调色板。使用set_palette()函数可以设置默认调色板,它内部同样调用color_palette(),接受相同的参数

为了充分利用color_palette()提供的各种选择,引入一个调色板的分类体系。总的来说,调色板分为三类:

  • 定性的调色板 (qualitative palettes),适用于类别型数据
  • 连续型调色板 (sequential palettes),适用于数值型数据
  • 发散型调色板 (diverging palettes),适用于有类别边界的数值型数据

定性的调色板

定性的调色板适用于类别型数据,因为主要的差别是色调。Seaborn的默认调色板是定性的调色板,有十个不同的颜色:

1
sns.color_palette()

这些颜色与matplotlib默认的调色板'tab10'顺序一样,只是没那么亮。比较一下:

1
sns.color_palette('tab10')

Seaborn有六种matplotlib调色板的变体:deepmutedpastelbrightdarkcolorblind。这些调色板覆盖了一系列平均亮度和饱和度:

很多人觉得中等亮度和饱和度的deep调色板很好看,但是也不那么容易区分。绘制出版用图片时要记得,这个调色板在有些情况下难以辨别。这个比较可以评估各个调色板在不同形式的色盲面前表现如何

圆形的颜色系统

当类别的数量很多时,为它们分配唯一色调最简单的方法是在圆形颜色空间 (保持亮度和饱和度不变,只改变色调)等距地提取颜色。如果要用的颜色数量超出当前调色板中的颜色数量,seaborn会默认采取这种方案

最常用的是hls颜色空间,它是RGB值的一种简单转换。这个调色板之前见过,就是绘制双变量直方图里的反例:

1
sns.color_palette('hls', 8)

基于人类视觉系统的工作原理,在RGB值上相同亮度和饱和度的颜色看起来未必浓淡相同。为了修复这个问题,seaborn提供了husl系统的界面,减小转动色彩盘时感受到的浓淡差异:

1
sns.color_palette('husl', 8)

如果seaborn要用的类别型调色板比当前设置的调色板包含更多颜色,会通过这种方式获取

类别型Color Brewer调色板

另一种好看的类别型调色板来自Color Brewer工具 (也有连续型和发散型调色板,后面会介绍):

1
sns.color_palette('Set2')

注意,定性的Color Brewer调色板有不同的长度,color_palette()默认返回完整的颜色列表:

1
sns.color_palette('Paired')

连续型调色板

第二种主要的调色板是连续型的。这种调色板适用于数据从较低或不那么重要的值变化到较高或重要的值 (反之亦然)。正如上面看到的那样,连续型调色板变化的主要维度是亮度。有些seaborn函数默认使用连续型调色板映射数值型数据。(由于历史原因,在relplot()或者displot()函数中,类别型和数值型映射都使用hue参数指定,尽管数值型映射使用的是没什么色调变化的调色板)

视觉上均匀的调色板

由于要展示数值型数据,最好的连续型调色板是视觉上均匀的,也就是说,两个颜色的区分度与两个数据的值的差异成比例。Seaborn有四个视觉上均匀的连续型调色板:rocketmakoflarecrest。前两个的亮度范围很宽,适用于热图等把颜色填充进去的情况:

1
sns.color_palette('rocket', as_cmap=True)

1
sns.color_palette('mako', as_cmap=True)

这些调色板的极值接近白色,因此不适用于填充线或者点等元素:很难在白色或者灰色背景下分辨出重要的值。这种情况下'flare''crest'是更好的选择。它们的亮度变化范围小一些,代价是色调变化略微明显一点。默认的亮度变化方向也进行了翻转,比较小的值颜色较浅:

1
sns.color_palette('flare', as_cmap=True)

1
sns.color_palette('crest', as_cmap=True)

也可以用matplotlib提供的视觉上均匀的调色板,比如'magma''viridis'

1
sns.color_palette('magma', as_cmap=True)

1
sns.color_palette('viridis', as_cmap=True)

作为matplotlib的传统,每个连续型调色板都有一个翻转的版本,需要添加后缀'_r'

1
sns.color_palette('rocket_r', as_cmap=True)

离散的映射 vs. 连续的映射

Seaborn可以从连续型调色板中产生离散的值,这时不会返回极值。比较一下离散版本的'rocket'和上面的连续版本:

1
sns.color_palette('rocket')

Seaborn会对类别型数据使用离散版本,对数值型数据使用连续版本。离散版本的连续型调色板也适用于存在顺序的类别型数据,尤其是在有一些色调变化的情况下

连续型cubehelix调色板

视觉上均匀的调色板很难通过编程产生,因为它不是基于RGB颜色空间。cubehelix提供了基于RGB的折中方案:通过线性增大或减小亮度及一些颜色的连续变化产生连续的调色板。尽管不是完美的视觉上均匀,生成的调色板也有很多优点。重要的是,设计过程的很多方面都可以使用参数调节

Matplotlib有默认的cubehelix版本:

1
sns.color_palette('cubehelix', as_cmap=True)

Seaborn的cubehelix_palette()函数返回的默认调色板与matplotlib有一些不同,因为它并不把色彩盘旋转那么大角度,也不覆盖那么大的深浅范围,并且翻转了亮度变化方向:

1
sns.cubehelix_palette(as_cmap=True)

cubehelix_palette()的其它参数控制调色板的样子。主要调整的是start (取值范围是0到3)和rot,也就是旋转的数量 (任意值,通常取-1到1)

1
sns.cubehelix_palette(start=0.5, rot=-0.5, as_cmap=True)

旋转得越多,色调变化越大:

1
sns.cubehelix_palette(start=0.5, rot=-0.75, as_cmap=True)

也可以控制两端的值多亮或多暗,以及是否翻转:

1
sns.cubehelix_palette(start=2, rot=0, dark=0, light=0.95, reverse=True, as_cmap=True)

color_palette()接受字符串编码,以'ch:'开头,可以产生任意cubehelix调色板。可以在字符串中传递参数的名字:

1
sns.color_palette('ch:start=0.2,rot=-0.3', as_cmap=True)

简洁起见,每个参数可以用它的首字母指定:

1
sns.color_palette('ch:s=-0.2,r=0.6', as_cmap=True)

自定义连续型调色板

使用light_palette()dark_palette()可以更简便地自定义连续型调色板,它们都是以一个颜色作为种子,产生一个逐渐变亮或逐渐变暗的调色板:

1
sns.light_palette('seagreen', as_cmap=True)

1
sns.dark_palette('#69d', reverse=True, as_cmap=True)

与cubehelix调色板一样,也可以通过color_palette()或者其它可以接受palette参数的函数指定这两种调色板:

1
sns.color_palette('light:b', as_cmap=True)

添加'_r'翻转调色板:

1
sns.color_palette('dark:salmon_r', as_cmap=True)

连续型Color Brewer调色板

Color Brewer库也有很多不错的连续型调色板。有些调色板有一个主色调:

1
sns.color_palette('Blues', as_cmap=True)

也有包含多个色调的调色板:

1
sns.color_palette('YLOrBr', as_cmap=True)

发散型调色板

第三种调色板是发散型的,适用于较大的数值和较小的数值都很重要,但是中间值没那么重要的数据。选择发散型调色板的原则与选择连续型调色板类似,除了调色板中需要有两个主要的色调,每一个主色调都位于极值附近。起始数值的亮度和饱和度也需要相近

视觉上均匀的发散型调色板

Seaborn有两个视觉上均匀的发散型调色板:vlagicefire。它们都使用蓝色和红色作为主色调,直观感觉是冷和热:

1
sns.color_palette('vlag', as_cmap=True)

1
sns.color_palette('icefire', as_cmap=True)

自定义发散型调色板

也可以使用seaborn函数diverging_palette()生成自定义的发散型调色板。这个函数使用'husl'颜色系统生成发散型调色板。需要传入两个色调 (单位是度),也可以传入极值的亮度和饱和度。使用'husl'意味着产生的调色板虽然不是完全视觉上均匀的,但会比较均衡:

1
sns.diverging_palette(220, 20, as_cmap=True)

这在你迷失在无聊的冷-热配色中时非常方便:

1
sns.diverging_palette(145, 300, s=60, as_cmap=True)

也可以制作中间值是暗色的调色板:

1
sns.diverging_palette(250, 30, l=65, center='dark', as_cmap=True)

注意,考虑到视觉障碍人士,要避免使用红绿配色

其它发散型调色板

Matplotlib中内置了一些不错的发散型调色板,包括Color Brewer调色板:

1
sns.color_palette('Spectral', as_cmap=True)

'coolwarm'调色板,中间值与极值的反差没那么大:

1
sns.color_palette('coolwarm', as_cmap=True)

在可视化过程中使用颜色的选择很多。Seaborn致力于不仅提供优秀的默认配色,同时也可以灵活地进行自定义

这些讨论只是一个开始,还有很多优秀的资源教我们如何在可视化过程中使用颜色。来自NASA Earth Observatory的一系列博文提供了很棒的例子。Matplotlib文档也有介绍他们调色板的教程