seaborn 绘图函数概览#
您与 seaborn 的大部分交互将通过一组绘图函数进行。教程后面的章节将探讨每个函数提供的具体功能。本章将从较高层次介绍您将遇到的不同类型的函数。
相似任务的相似功能#
seaborn 的命名空间是扁平的;所有功能都可以在顶层访问。但代码本身是分层结构的,包含通过不同方式实现相似可视化目标的函数模块。大多数文档都是围绕这些模块构建的:你将遇到诸如“relational”、“distributional”和“categorical”之类的名称。
例如,分布模块 定义了专门用于表示数据点分布的函数。这包括像直方图这样的熟悉方法:
penguins = sns.load_dataset("penguins")
sns.histplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
以及类似但可能不太熟悉的选项,例如核密度估计:
sns.kdeplot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
模块内的函数共享大量底层代码,并提供类似的功能,这些功能在库的其他组件中可能不存在(如上面的示例中的 multiple="stack")。它们旨在方便在探索数据集时切换不同的视觉表示,因为不同的表示通常具有互补的优缺点。
图级 vs. 轴级函数#
除了不同的模块外,seaborn 函数还有一个跨领域的分类,即“轴级”或“图级”。上面的例子是轴级函数。它们将数据绘制到单个 matplotlib.pyplot.Axes 对象上,这是函数的返回值。
相比之下,图级函数通过 seaborn 对象(通常是 FacetGrid)与 matplotlib 进行接口,该对象管理图形。每个模块都有一个单一的图级函数,它提供了一个统一的接口来访问其各种轴级函数。组织结构看起来有点像这样:
例如,displot() 是分布模块的图级函数。它的默认行为是绘制直方图,使用与 histplot() 相同的代码在幕后执行:
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack")
要绘制核密度图,使用与 kdeplot() 相同的代码,通过 kind 参数选择它:
sns.displot(data=penguins, x="flipper_length_mm", hue="species", multiple="stack", kind="kde")
你会注意到,图级别的图表看起来大部分像它们的轴级别的对应物,但有一些不同。特别是,图例被放置在图表的外部。它们的形状也略有不同(稍后会详细介绍)。
figure-level 函数提供的最有用的功能是它们可以轻松创建包含多个子图的图形。例如,与其将三种企鹅物种的分布堆叠在同一轴上,我们可以通过在图形的列中绘制每个分布来“分面”它们:
sns.displot(data=penguins, x="flipper_length_mm", hue="species", col="species")
图级函数包装了它们的轴级对应物,并将特定类型的关键字参数(如直方图的箱子大小)传递给底层函数。这意味着它们同样灵活,但有一个缺点:特定类型的参数不会出现在函数签名或文档字符串中。它们的一些功能可能不太容易被发现,你可能需要在文档的两个不同页面查看才能理解如何实现特定目标。
轴级函数生成自包含的图表#
轴级函数被编写为 matplotlib 函数的直接替代品。虽然它们会自动添加轴标签和图例,但它们不会修改绘制它们的轴之外的任何内容。这意味着它们可以组合成任意复杂的 matplotlib 图形,并产生可预测的结果。
轴级函数在内部调用 matplotlib.pyplot.gca() ,这会钩入 matplotlib 状态机接口,以便它们在“当前活动”的轴上绘制图形。但它们还接受一个 ax= 参数,该参数与面向对象的接口集成,并允许您精确指定每个图形的绘制位置:
f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw=dict(width_ratios=[4, 3]))
sns.scatterplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", ax=axs[0])
sns.histplot(data=penguins, x="species", hue="species", shrink=.8, alpha=.8, legend=False, ax=axs[1])
f.tight_layout()
图级函数拥有它们的图#
相比之下,图级函数不能(轻易地)与其他图组合。设计上,它们“拥有”自己的图,包括其初始化,因此没有使用图级函数在现有轴上绘图的概念。这一限制使得图级函数能够实现诸如将图例放在图外等功能。
尽管如此,通过访问它们返回的对象上的 matplotlib 轴,并以此方式添加其他元素到图中,可以超越图级函数所提供的内容:
tips = sns.load_dataset("tips")
g = sns.relplot(data=tips, x="total_bill", y="tip")
g.ax.axline(xy1=(10, 2), slope=.2, color="b", dashes=(5, 2))
自定义图表从图级别函数#
图级函数返回一个 FacetGrid 实例,该实例有一些方法用于以一种“智能”的方式自定义图形的属性,这种方式考虑到了子图的组织。例如,你可以使用一行代码来更改外部轴的标签:
g = sns.relplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", col="sex")
g.set_axis_labels("Flipper length (mm)", "Bill length (mm)")
虽然方便,但这确实增加了一些额外的复杂性,因为你需要记住这种方法不是 matplotlib API 的一部分,只有在使用图级函数时才存在。
指定图形尺寸#
要增加或减少 matplotlib 图表的大小,您可以设置整个图形的宽度和高度,无论是通过 全局 rcParams 在设置图表时(例如使用 matplotlib.pyplot.subplots() 的 figsize 参数),还是在图形对象上调用方法(例如 matplotlib.Figure.set_size_inches())。当使用 seaborn 中的轴级函数时,同样的规则适用:图表的大小由其所属图形的大小和该图形中的轴布局决定。
在使用图层级函数时,有几个关键的不同之处。首先,这些函数本身有控制图形大小的参数(尽管这些实际上是底层管理图形的 FacetGrid 的参数)。其次,这些参数 height 和 aspect 与 matplotlib 中的 width、height 参数化方式略有不同(使用 seaborn 参数,width = height * aspect)。最重要的是,这些参数对应于每个 子图 的大小,而不是整个图形的大小。
为了说明这些方法之间的区别,这里是一个子图的默认输出 matplotlib.pyplot.subplots():
f, ax = plt.subplots()
一个包含多列的图形将具有相同的整体大小,但轴将被水平压缩以适应空间:
f, ax = plt.subplots(1, 2, sharey=True)
相比之下,由图层级函数创建的图将是方形的。为了演示这一点,让我们直接使用 FacetGrid 来设置一个空图。这在 relplot()、displot() 或 catplot() 等函数中在幕后发生:
g = sns.FacetGrid(penguins)
当添加额外的列时,图形本身会变得更宽,以便其子图具有相同的大小和形状:
g = sns.FacetGrid(penguins, col="sex")
并且您可以调整每个子图的大小和形状,而不必考虑图形中总行数和列数:
g = sns.FacetGrid(penguins, col="sex", height=3.5, aspect=.75)
结果是,你可以在不停止思考如何调整总图大小的前提下分配分面变量。一个缺点是,当你确实想要改变图形大小时,你需要记住这与在 matplotlib 中的操作方式有些不同。
图层级函数的相对优点#
以下是我们上面讨论的优缺点总结:
优势 |
缺点 |
|---|---|
通过数据变量轻松分面 |
许多参数不在函数签名中 |
默认情况下图例在图外 |
不能作为更大的 matplotlib 图形的一部分 |
简单的图形级自定义 |
与 matplotlib 不同的 API |
不同的图形大小参数化 |
不同的图形大小参数化 |
总的来说,图级函数增加了一些额外的复杂性,这可能会让初学者感到困惑,但它们独特的功能赋予了它们额外的力量。教程文档主要使用图级函数,因为它们生成的图稍微更清晰,我们通常推荐在大多数应用中使用它们。它们不适合的一个情况是你需要制作一个复杂的、独立的图,该图组合了多种不同的绘图类型。在这种情况下,建议直接使用 matplotlib 设置图,并使用轴级函数填充各个组件。
结合数据的多个视图#
在 seaborn 中,有两个重要的绘图函数并不完全符合上述分类方案。这些函数,jointplot() 和 pairplot(),利用来自不同模块的多种类型的图表来在一个图形中表示数据集的多个方面。这两种图表都是图形级函数,默认情况下会创建包含多个子图的图形。但它们使用不同的对象来管理图形:分别是 JointGrid 和 PairGrid。
jointplot() 绘制两个变量之间的关系或联合分布,同时添加边缘轴,分别显示每个变量的单变量分布:
sns.jointplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species")
pairplot() 与此类似 — 它结合了联合视图和边缘视图 — 但不是专注于单一关系,而是同时可视化所有变量的成对组合:
sns.pairplot(data=penguins, hue="species")
在幕后,这些函数正在使用你已经遇到过的轴级函数(scatterplot() 和 kdeplot()),它们还有一个 kind 参数,可以让你快速切换到不同的表示方式:
sns.jointplot(data=penguins, x="flipper_length_mm", y="bill_length_mm", hue="species", kind="hist")