构建结构化的多图网格#
在探索多维数据时,一种有用的方法是在数据集的不同子集上绘制相同图表的多个实例。这种技术有时被称为“格子”或“网格”绘图,它与“小倍数”的概念有关。它允许观看者快速提取有关复杂数据集的大量信息。Matplotlib 提供了对制作具有多个轴的图形的良好支持;seaborn 在此基础上构建,将图表的结构直接链接到数据集的结构。
本教程章节中讨论的对象之上构建了图形级函数。在大多数情况下,您需要使用这些函数。它们处理一些重要的簿记,这些簿记会同步每个网格中的多个图。本章解释了底层对象的工作原理,这可能对高级应用程序有用。
条件小倍数#
FacetGrid
类在您想要可视化数据集子集中的变量分布或多个变量之间的关系时很有用。可以绘制带有最多三个维度的FacetGrid
:row
、col
和 hue
。前两个与生成的轴数组有明显的对应关系;将 hue 变量视为沿深度轴的第三维,其中不同的级别以不同的颜色绘制。
每个 relplot()
、displot()
、catplot()
和 lmplot()
在内部使用此对象,并在完成时返回此对象,以便可以用于进一步调整。
使用数据框和将形成网格行、列或色调维度的变量名称来初始化FacetGrid
对象,该类用于初始化。这些变量应该是分类的或离散的,然后变量每个级别的数据将用于沿该轴的刻面。例如,假设我们想要检查 tips
数据集中午餐和晚餐之间的差异
tips = sns.load_dataset("tips")
g = sns.FacetGrid(tips, col="time")
像这样初始化网格会设置 matplotlib 图形和轴,但不会在上面绘制任何内容。
在这个网格上可视化数据的最主要方法是使用 FacetGrid.map()
方法。向其提供一个绘图函数和数据框中要绘制的变量名称。让我们看看这些子集中的每一个中小费的分布,使用直方图
g = sns.FacetGrid(tips, col="time")
g.map(sns.histplot, "tip")
此函数将绘制图形并注释轴,希望在一步骤中生成完成的图。要制作关系图,只需传递多个变量名称。您还可以提供关键字参数,这些参数将传递给绘图函数
g = sns.FacetGrid(tips, col="sex", hue="smoker")
g.map(sns.scatterplot, "total_bill", "tip", alpha=.7)
g.add_legend()
有几个选项可以控制网格的外观,这些选项可以传递给类构造函数。
g = sns.FacetGrid(tips, row="smoker", col="time", margin_titles=True)
g.map(sns.regplot, "size", "total_bill", color=".3", fit_reg=False, x_jitter=.1)
请注意,margin_titles
没有被 matplotlib API 正式支持,并且可能在所有情况下都无法正常工作。特别是,它目前无法与位于图之外的图例一起使用。
图形的大小通过提供每个刻面的高度以及纵横比来设置
g = sns.FacetGrid(tips, col="day", height=4, aspect=.5)
g.map(sns.barplot, "sex", "total_bill", order=["Male", "Female"])
刻面的默认顺序是从数据框中的信息推导出来的。如果用于定义刻面的变量具有分类类型,则使用类别顺序。否则,刻面将按类别级别出现的顺序排列。但是,可以使用适当的 *_order
参数指定任何刻面维度的排序
ordered_days = tips.day.value_counts().index
g = sns.FacetGrid(tips, row="day", row_order=ordered_days,
height=1.7, aspect=4,)
g.map(sns.kdeplot, "total_bill")
可以提供任何 seaborn 调色板(即可以传递给 color_palette()
的任何内容)。您还可以使用将 hue
变量中值的名称映射到有效 matplotlib 颜色的字典
pal = dict(Lunch="seagreen", Dinner=".7")
g = sns.FacetGrid(tips, hue="time", palette=pal, height=5)
g.map(sns.scatterplot, "total_bill", "tip", s=100, alpha=.5)
g.add_legend()
如果您有一个变量的多个级别,您可以沿列绘制它,但将其“包装”起来,以便它们跨越多行。这样做时,您不能使用 row
变量。
attend = sns.load_dataset("attention").query("subject <= 12")
g = sns.FacetGrid(attend, col="subject", col_wrap=4, height=2, ylim=(0, 10))
g.map(sns.pointplot, "solutions", "score", order=[1, 2, 3], color=".3", errorbar=None)
使用 FacetGrid.map()
绘制图形后(可以多次调用它),您可能想要调整图形的某些方面。FacetGrid
对象上还有许多用于以更高抽象级别操作图形的方法。最通用的方法是 FacetGrid.set()
,还有其他更专业的方法,例如 FacetGrid.set_axis_labels()
,它尊重内部刻面没有轴标签的事实。例如
with sns.axes_style("white"):
g = sns.FacetGrid(tips, row="sex", col="smoker", margin_titles=True, height=2.5)
g.map(sns.scatterplot, "total_bill", "tip", color="#334488")
g.set_axis_labels("Total bill (US Dollars)", "Tip")
g.set(xticks=[10, 30, 50], yticks=[2, 6, 10])
g.figure.subplots_adjust(wspace=.02, hspace=.02)
为了进行更多自定义,您可以直接使用底层的 matplotlib Figure
和 Axes
对象,它们分别存储为成员属性,位于 figure
和 axes_dict
。在制作没有行或列刻面的图形时,您还可以使用 ax
属性直接访问单个轴。
g = sns.FacetGrid(tips, col="smoker", margin_titles=True, height=4)
g.map(plt.scatter, "total_bill", "tip", color="#338844", edgecolor="white", s=50, lw=1)
for ax in g.axes_dict.values():
ax.axline((0, 0), slope=.2, c=".2", ls="--", zorder=0)
g.set(xlim=(0, 60), ylim=(0, 14))
使用自定义函数#
在使用 FacetGrid
时,您并不局限于现有的 matplotlib 和 seaborn 函数。但是,要正常工作,您使用的任何函数都必须遵循以下规则
它必须绘制到“当前活动”matplotlib
Axes
上。对于matplotlib.pyplot
命名空间中的函数,这将是正确的,如果要直接使用其方法,可以调用matplotlib.pyplot.gca()
获取对当前Axes
的引用。它必须在位置参数中接受它绘制的数据。在内部,
FacetGrid
将为传递给FacetGrid.map()
的每个命名位置参数传递一个Series
数据。它必须能够接受
color
和label
关键字参数,并且理想情况下,它会对它们做一些有用的事情。在大多数情况下,最容易捕捉一个通用的**kwargs
字典并将其传递给底层绘图函数。
让我们看看您可以绘制的函数的最小示例。此函数将只为每个刻面获取一个数据向量
from scipy import stats
def quantile_plot(x, **kwargs):
quantiles, xr = stats.probplot(x, fit=False)
plt.scatter(xr, quantiles, **kwargs)
g = sns.FacetGrid(tips, col="sex", height=4)
g.map(quantile_plot, "total_bill")
如果我们想要制作一个双变量图,您应该编写函数,使其首先接受 x 轴变量,然后接受 y 轴变量
def qqplot(x, y, **kwargs):
_, xr = stats.probplot(x, fit=False)
_, yr = stats.probplot(y, fit=False)
plt.scatter(xr, yr, **kwargs)
g = sns.FacetGrid(tips, col="smoker", height=4)
g.map(qqplot, "total_bill", "tip")
因为 matplotlib.pyplot.scatter()
接受 color
和 label
关键字参数并对它们做正确的事情,我们可以毫无困难地添加一个色调刻面
g = sns.FacetGrid(tips, hue="time", col="sex", height=4)
g.map(qqplot, "total_bill", "tip")
g.add_legend()
但是,有时您需要映射一个不能按预期方式使用 color
和 label
关键字参数工作的函数。在这种情况下,您需要显式地捕获它们并在自定义函数的逻辑中处理它们。例如,这种方法将允许我们映射 matplotlib.pyplot.hexbin()
,否则它无法与 FacetGrid
API 很好地配合使用
def hexbin(x, y, color, **kwargs):
cmap = sns.light_palette(color, as_cmap=True)
plt.hexbin(x, y, gridsize=15, cmap=cmap, **kwargs)
with sns.axes_style("dark"):
g = sns.FacetGrid(tips, hue="time", col="time", height=4)
g.map(hexbin, "total_bill", "tip", extent=[0, 50, 0, 10]);
绘制成对数据关系#
PairGrid
还允许您使用相同的图类型快速绘制一个小的子图网格,以可视化每个子图中的数据。在 PairGrid
中,每行和每列都分配给不同的变量,因此生成的图显示了数据集中每个成对关系。这种风格的图有时被称为“散点图矩阵”,因为这是显示每个关系的最常见方法,但 PairGrid
不限于散点图。
了解 FacetGrid
和 PairGrid
之间的区别很重要。在前者中,每个小图显示了相同的关系,但受其他变量的不同水平的影响。在后者中,每个图显示了不同的关系(尽管上三角形和下三角形将具有镜像的图)。使用 PairGrid
可以让你非常快速地获得数据集内有趣关系的非常高级的概览。
该类的基本用法与 FacetGrid
非常相似。首先初始化网格,然后将绘图函数传递给 map
方法,该方法将在每个子图上调用该函数。还有一个配套函数,pairplot()
,它在一定程度上牺牲了灵活性以换取更快的绘图速度。
iris = sns.load_dataset("iris")
g = sns.PairGrid(iris)
g.map(sns.scatterplot)
可以在对角线上绘制不同的函数,以显示每一列中变量的单变量分布。注意,轴刻度将不会对应于该图的计数或密度轴。
g = sns.PairGrid(iris)
g.map_diag(sns.histplot)
g.map_offdiag(sns.scatterplot)
使用这种图的一种非常常见的方式是通过一个单独的分类变量对观察结果进行着色。例如,鸢尾花数据集对三种不同种类的鸢尾花中的每一种都包含四个测量值,因此你可以看到它们的不同之处。
g = sns.PairGrid(iris, hue="species")
g.map_diag(sns.histplot)
g.map_offdiag(sns.scatterplot)
g.add_legend()
默认情况下,数据集中的每个数值列都被使用,但如果你想要的话,你可以专注于特定的关系。
g = sns.PairGrid(iris, vars=["sepal_length", "sepal_width"], hue="species")
g.map(sns.scatterplot)
也可以在上三角形和下三角形中使用不同的函数来强调关系的不同方面。
g = sns.PairGrid(iris)
g.map_upper(sns.scatterplot)
g.map_lower(sns.kdeplot)
g.map_diag(sns.kdeplot, lw=3, legend=False)
具有对角线上的恒等关系的正方形网格实际上只是一个特例,你可以在行和列中绘制不同的变量。
g = sns.PairGrid(tips, y_vars=["tip"], x_vars=["total_bill", "size"], height=4)
g.map(sns.regplot, color=".3")
g.set(ylim=(-1, 11), yticks=[0, 5, 10])
当然,美学属性是可以配置的。例如,你可以使用不同的调色板(例如,为了显示 hue
变量的排序),并将关键字参数传递给绘图函数。
g = sns.PairGrid(tips, hue="size", palette="GnBu_d")
g.map(plt.scatter, s=50, edgecolor="white")
g.add_legend()
PairGrid
很灵活,但要快速查看数据集,使用 pairplot()
可能更容易。该函数默认使用散点图和直方图,虽然还会添加一些其他类型(目前,你也可以在非对角线上绘制回归图,在对角线上绘制 KDE)。
sns.pairplot(iris, hue="species", height=2.5)
你也可以使用关键字参数控制图的美学属性,它会返回 PairGrid
实例,以便进行进一步调整。
g = sns.pairplot(iris, hue="species", palette="Set2", diag_kind="kde", height=2.5)