选择调色板#

Seaborn 使得使用适合您数据特征和可视化目标的颜色变得容易。本章讨论了指导您选择的总体原则,以及 seaborn 中帮助您快速找到给定应用最佳解决方案的工具。

使用颜色绘图的一般原则#

颜色组成部分#

由于我们的眼睛工作方式,一种特定的颜色可以用三个分量来定义。我们通常通过指定其RGB值在计算机中编程颜色,这些值设置了显示器中红色、绿色和蓝色通道的强度。但对于分析颜色的感知属性,最好从*色调*、*饱和度*和*亮度*通道的角度来思考。

色调是非技术意义上区分“不同颜色”的成分。它是颜色的属性,导致像“红色”和“蓝色”这样的第一级名称:

饱和度(或色度)是 色彩丰富度 。当两种颜色具有更多饱和度时,即使它们的色调不同,它们看起来也会更加明显:

而亮度对应于发出的光量(或印刷颜色的反射量),范围从黑色到白色:

改变色调以区分类别#

当你想在图表中表示多个类别时,通常应该改变元素的颜色。考虑这个简单的例子:在这两个图表中,哪一个更容易数出三角形点的数量?

../_images/color_palettes_9_0.png

在右边的图中,橙色三角形“跳出”,使得它们很容易与圆形区分开来。这种跳出效果发生是因为我们的视觉系统优先考虑颜色差异。

蓝色和橙色在色调上差异最大。色调对于表示类别很有用:大多数人可以相对容易地区分一定数量的色调,并且具有不同色调但亮度或强度相似的点似乎同样重要。它还使图表更容易讨论。考虑这个例子:

../_images/color_palettes_11_0.png

大多数人能够迅速确定左侧图中有五个不同的类别,如果被要求描述“蓝色”点,他们也能够做到。

在右边的图中,所有的点都是蓝色的,但它们的亮度和饱和度不同,很难说有多少个独特的类别。我们如何描述一个特定的类别呢?“那些相当蓝但又不过于蓝的点?”此外,灰色的点似乎融入了背景,相对于更强烈的蓝色点,它们被相对地淡化了。如果这些类别同样重要,这是一个糟糕的表示。

因此,作为一般规则,使用色调变化来表示类别。话虽如此,这里有一些注意事项。如果你的图中使用了超过几种颜色,可能会变得难以记住每种颜色代表的含义,除非类别与所使用的颜色之间存在预先的关联。这使得你的图更难解释:观众不得不不断参考图例来理解所展示的内容,而不是专注于数据本身。因此,你应该尽量避免制作过于复杂的图表。同时,要注意并非每个人对颜色的感知都是一样的。同时改变形状(或其他属性)和颜色可以帮助色觉异常的人理解你的图表,并且在打印成黑白时,图表仍能保持(一定程度上)可解释性。

改变亮度以表示数字#

另一方面,色调变化不太适合表示数值数据。考虑这个例子,我们需要颜色来表示双变量直方图中的计数。在左边,我们使用圆形色图,其中每个箱子内观察数量的逐渐变化对应于色调的逐渐变化。在右边,我们使用一种调色板,用更亮的颜色表示计数更大的箱子:

../_images/color_palettes_14_0.png

使用基于色调的调色板,很难确定双变量分布的形状。相比之下,亮度调色板使两个显著峰值更加清晰。

变化的亮度有助于你看到数据中的结构,亮度的变化更直观地被处理为重要性的变化。但右边的图没有使用灰度色图。它的色彩使其更有趣,微妙的色调变化增加了两个值之间的感知距离。因此,小的差异稍微更容易分辨。

这些例子表明,色彩搭配的选择不仅仅是关于美学:你选择的色彩如果使用得当,可以揭示数据中的模式;如果使用不当,则会掩盖这些模式。没有一种最佳的调色板,但对于特定的数据集和可视化方法,有些调色板会更合适或更不合适。

美学确实重要:人们越想看你的图表,他们从中学到东西的机会就越大。即使你是在为自己制作图表,这也是正确的。在探索性数据分析过程中,你可能会生成许多类似的图表。改变调色板会增加一种新鲜感,这会让你保持专注,并准备好注意到数据的有趣特征。

那么,你如何选择既能很好地代表你的数据又看起来吸引人的调色板呢?

选择调色板的工具#

使用调色板最重要的功能是, 恰当地, color_palette()。这个函数提供了大多数在 seaborn 中生成调色板的可能方式的接口。并且它被任何具有 palette 参数的函数内部使用。

color_palette() 的主要参数通常是一个字符串:可以是特定调色板的名字,或者是系列名和选择特定成员的附加参数。在后一种情况下,color_palette() 会委托给更具体的函数,例如 cubehelix_palette()。也可以传递一个颜色列表,这些颜色可以以 matplotlib 接受的任何方式指定(RGB 元组、十六进制代码或 X11 表中的名称)。返回值是一个对象,它包装了一个 RGB 元组列表,并带有几个有用的方法,例如转换为十六进制代码和丰富的 HTML 表示。

调用 color_palette() 时不带参数将返回当前 matplotlib(以及大多数 seaborn 函数)在未另行指定颜色时使用的默认颜色调色板。可以通过相应的 set_palette() 函数设置此默认调色板,该函数在内部调用 color_palette() 并接受相同的参数。

为了激发 color_palette() 提供的不同选项,引入一个颜色调色板的分类方案将是有用的。大致上,调色板可以分为三类之一:

  • 定性调色板,适合表示分类数据

  • 顺序调色板,适合表示数值数据

  • 发散调色板,适用于表示具有分类边界的数值数据

定性色彩调色板#

定性调色板非常适合表示分类数据,因为它们的大部分变化在于色调成分。seaborn 中的默认颜色调色板是一个包含十个不同色调的定性调色板:

sns.color_palette()

这些颜色的顺序与默认的 matplotlib 颜色调色板 "tab10" 相同,但它们的强度稍低。比较:

sns.color_palette("tab10")

Seaborn 实际上有六种 matplotlib 调色板的变体,分别称为 deepmutedpastelbrightdarkcolorblind。这些变体涵盖了不同的平均亮度和饱和度值:

../_images/color_palettes_22_0.svg

许多人发现默认的 "deep" 调色板的适度色调在美学上令人愉悦,但它们也较为不明显。因此,在某些情况下,它们可能更难以区分,这在制作出版图形时需要牢记。这个比较 可以帮助估计在模拟不同形式色盲时,seaborn 调色板的表现。

使用圆形色彩系统#

当你有任意数量的类别时,找到独特色调的最简单方法是绘制圆形颜色空间中均匀间隔的颜色(其中色调变化而保持亮度和饱和度不变)。这就是大多数 seaborn 函数在需要使用的颜色数量超过当前默认颜色循环中设置的颜色数量时默认采用的方法。

最常见的方法是使用 hls 色彩空间,这是对RGB值的简单转换。我们之前在绘制直方图时将其作为一个反例:

sns.color_palette("hls", 8)

由于人类视觉系统的工作方式,RGB值中具有相同亮度和饱和度的颜色不一定看起来同样强烈。为了解决这个问题,seaborn提供了一个接口到 husl 系统(现已更名为HSLuv),该系统在你围绕色轮旋转时实现了较少的强度变化:

sns.color_palette("husl", 8)

当 seaborn 需要一个颜色数量超过当前默认值的分类调色板时,它将使用这种方法。

使用分类的 Color Brewer 调色板#

另一个视觉上令人愉悦的分类调色板来源是 Color Brewer 工具(它还提供了顺序和发散调色板,我们将在下面看到)。

sns.color_palette("Set2")

请注意,定性的 Color Brewer 调色板有不同的长度,而 color_palette() 的默认行为是给你完整的列表:

sns.color_palette("Paired")

顺序颜色调色板#

第二种主要调色板类别称为“顺序”。这种映射适用于数据范围从相对较低或不感兴趣的值到相对较高或感兴趣的值(或反之亦然)。如上所述,顺序调色板中的主要变化维度是亮度。当您在映射数值数据时,某些 seaborn 函数将默认使用顺序调色板。(由于历史原因,分类和数值映射在 relplot()displot() 等函数中都使用 hue 参数指定,即使数值映射使用色调变化相对较小的调色板)。

感知上均匀的调色板#

因为它们旨在表示数值,最佳的顺序调色板将是 感知均匀 的,这意味着两种颜色的相对可辨别性与其对应数据值的差异成正比。Seaborn 包含了四种感知均匀的顺序调色板:"rocket""mako""flare""crest"。前两种具有非常宽的亮度范围,非常适合应用于热图等场景,其中颜色填充了它们所绘制的空间:

sns.color_palette("rocket", as_cmap=True)
rocket color map
sns.color_palette("mako", as_cmap=True)
mako color map

由于这些色图的极值接近白色,因此它们不适合用于为线条或点等元素着色:在白色或灰色背景下,将难以区分重要值。”flare” 和 “crest” 色图是此类绘图的更好选择。它们的亮度变化范围更受限制,但通过稍微更明显的色调变化来弥补。亮度的默认方向也被反转,因此较小的值具有较浅的颜色:

sns.color_palette("flare", as_cmap=True)
flare color map
sns.color_palette("crest", as_cmap=True)
crest color map

也可以使用 matplotlib 提供的感知均匀的色图,例如 "magma""viridis"

sns.color_palette("magma", as_cmap=True)
magma color map
sns.color_palette("viridis", as_cmap=True)
viridis color map

与 matplotlib 的惯例一样,每个连续的颜色映射都有一个反转版本,其后缀为 "_r"

sns.color_palette("rocket_r", as_cmap=True)
rocket_r color map

离散 vs. 连续映射#

需要注意的一点是,seaborn 可以从连续的色图中生成离散的值,并且在这样做时,它不会使用最极端的值。比较离散版本的 "rocket" 与上面显示的连续版本:

sns.color_palette("rocket")

在内部,seaborn 对分类数据使用离散版本,在数值映射模式下使用连续版本。离散顺序颜色映射非常适合用于可视化具有内在顺序的分类数据,特别是当存在一些色调变化时。

顺序“cubehelix”调色板#

感知上均匀的色图很难通过编程生成,因为它们不是基于RGB色彩空间的。cubehelix 系统提供了一个基于RGB的折中方案:它生成的顺序调色板具有线性增加或减少的亮度和一些连续的色调变化。虽然不是完全感知上均匀,但生成的色图具有许多良好的特性。重要的是,设计过程的许多方面是可参数化的。

Matplotlib 内置了默认的 cubehelix 版本:

sns.color_palette("cubehelix", as_cmap=True)
cubehelix color map

seaborn 的 cubehelix_palette() 函数返回的默认调色板与 matplotlib 的默认调色板有些不同,因为它在色调轮上旋转的幅度较小,覆盖的强度范围也较窄。它还反转了亮度斜坡:

sns.cubehelix_palette(as_cmap=True)
seaborn_cubehelix color map

其他传递给 cubehelix_palette() 的参数控制调色板的样子。你主要会改变的两个参数是 start``(一个介于0和3之间的值)和 ``rot,或旋转次数(一个任意值,但通常介于-1和1之间)

sns.cubehelix_palette(start=.5, rot=-.5, as_cmap=True)
seaborn_cubehelix color map

你旋转得越多,你看到的色调变化就越多:

sns.cubehelix_palette(start=.5, rot=-.75, as_cmap=True)
seaborn_cubehelix color map

你可以控制端点的深浅程度及其顺序:

sns.cubehelix_palette(start=2, rot=0, dark=0, light=.95, reverse=True, as_cmap=True)
seaborn_cubehelix color map

The color_palette() 接受一个以 "ch:" 开头的字符串代码,用于生成任意 cubehelix 调色板。你可以在字符串中传递参数名称:

sns.color_palette("ch:start=.2,rot=-.3", as_cmap=True)
seaborn_cubehelix color map

为了简洁,每个参数都可以用其首字母指定:

sns.color_palette("ch:s=-.2,r=.6", as_cmap=True)
seaborn_cubehelix color map

自定义顺序调色板#

对于一个更简单的自定义顺序调色板接口,你可以使用 light_palette()dark_palette(),它们都是从一个单一颜色开始,并生成一个从浅色或深色不饱和值渐变到该颜色的调色板:

sns.light_palette("seagreen", as_cmap=True)
blend color map
sns.dark_palette("#69d", reverse=True, as_cmap=True)
blend color map

与cubehelix调色板一样,您也可以通过 color_palette() 或任何接受 palette 的地方指定浅色或深色调色板:

sns.color_palette("light:b", as_cmap=True)
blend color map

通过添加 "_r" 来反转颜色映射:

sns.color_palette("dark:salmon_r", as_cmap=True)
blend color map

顺序颜色 Brewer 调色板#

Color Brewer 库还提供了一些适合顺序调色板的好选项。它们包括具有一种主要色调的调色板:

sns.color_palette("Blues", as_cmap=True)
Blues color map

以及多色调选项:

sns.color_palette("YlOrBr", as_cmap=True)
YlOrBr color map

发散色板#

第三类调色板称为“发散型”。这些用于数据中,其中低值和高值都很有趣,并且跨越一个应弱化的中点值(通常为0)。选择好的发散型调色板的规则类似于好的顺序型调色板,只是现在色图中应该有两个主导色调,一个在(或接近)每个极点。同样重要的是,起始值具有相似的亮度和饱和度。

感知上均匀的分裂调色板#

Seaborn 包含两个感知上均匀的分散调色板:"vlag""icefire"。它们都在两极使用蓝色和红色,许多人直观地将其处理为“冷”和“热”:

sns.color_palette("vlag", as_cmap=True)
vlag color map
sns.color_palette("icefire", as_cmap=True)
icefire color map

自定义发散调色板#

你也可以使用 seaborn 函数 diverging_palette() 来为发散数据创建一个自定义的色图。这个函数使用 husl 颜色系统来制作发散调色板。你传递给它两个色调(以度为单位),以及可选的极端值的亮度和饱和度。使用 husl 意味着极端值和由此产生的向中点的渐变,虽然不是完全感知均匀,但会是平衡良好的:

sns.diverging_palette(220, 20, as_cmap=True)
blend color map

当你想摆脱冷热方法的枯燥限制时,这很方便:

sns.diverging_palette(145, 300, s=60, as_cmap=True)
blend color map

也可以制作一个调色板,其中中点是暗色而不是亮色:

sns.diverging_palette(250, 30, l=65, center="dark", as_cmap=True)
blend color map

这里需要强调的是,尽管红色和绿色很直观,但 应该避免使用

其他发散调色板#

matplotlib 内置了一些其他很好的发散调色板,包括 Color Brewer 调色板:

sns.color_palette("Spectral", as_cmap=True)
Spectral color map

coolwarm 调色板,它在中间值和极端值之间的对比度较低:

sns.color_palette("coolwarm", as_cmap=True)
coolwarm color map

如你所见,在可视化中使用颜色的选项有很多。Seaborn 既尝试使用良好的默认值,又提供大量的灵活性。

这个讨论只是一个开始,关于在可视化中使用颜色的技巧,还有很多好的资源可以学习。一个很好的例子是NASA地球观测站的这一系列博客文章 series of blog posts。matplotlib文档中也有一个很好的教程 nice tutorial,展示了他们颜色映射的一些感知特性。