可视化分类数据#

关系图教程 中,我们看到了如何使用不同的视觉表示来显示数据集中的多个变量之间的关系。在示例中,我们重点关注两个数值变量之间存在主要关系的情况。如果主要变量之一是“分类的”(划分为离散组),那么使用更专门的可视化方法可能会有所帮助。

在 seaborn 中,有多种方法可以可视化涉及分类数据的关系列。类似于 relplot()scatterplot()lineplot() 之间的关系,有两种方法可以绘制这些图。有一些轴级函数用于以不同的方式绘制分类数据,以及一个图级界面 catplot(),它为它们提供了统一的更高级别的访问权限。

将不同的分类绘图类型视为属于三个不同的类别是有帮助的,我们将在下面详细讨论它们。它们是

分类散点图

分类分布图

分类估计图

这些类别使用不同级别的粒度来表示数据。在决定使用哪个时,您需要考虑要回答的问题。统一的 API 使在不同类型之间轻松切换并从多个角度查看数据变得容易。

在本教程中,我们将主要关注图级界面 catplot()。请记住,此函数是上述每个函数的更高级别界面,因此我们将在显示每种类型的图时引用它们,同时保留更详细的特定于类型的 API 文档。

分类散点图#

catplot() 中,数据默认表示使用散点图。实际上,seaborn 中有两个不同的分类散点图。它们采用不同的方法来解决用散点图表示分类数据的最大挑战,即属于同一类别的所有点都会落在对应于分类变量的轴上的相同位置。由 stripplot() 使用的方法(这是 catplot() 中的默认“kind”)是通过少量随机的“抖动”来调整分类轴上点的 位置。

tips = sns.load_dataset("tips")
sns.catplot(data=tips, x="day", y="total_bill")
../_images/categorical_3_0.png

jitter 参数控制抖动的幅度或完全禁用抖动。

sns.catplot(data=tips, x="day", y="total_bill", jitter=False)
../_images/categorical_5_0.png

第二种方法使用一种算法来调整分类轴上的点,该算法可以防止它们重叠。它可以更好地表示观测值的分布,尽管它只适用于相对较小的数据集。这种类型的图有时被称为“蜂群图”,由 seaborn 中的 swarmplot() 绘制,通过在 catplot() 中设置 kind="swarm" 来激活。

sns.catplot(data=tips, x="day", y="total_bill", kind="swarm")
../_images/categorical_7_0.png

类似于关系图,可以通过使用 hue 语义将另一个维度添加到分类图中。(分类图目前不支持 sizestyle 语义)。每个不同的分类绘图函数都以不同的方式处理 hue 语义。对于散点图,只需要更改点的颜色。

sns.catplot(data=tips, x="day", y="total_bill", hue="sex", kind="swarm")
../_images/categorical_9_0.png

与数值数据不同,分类变量在其轴上的级别如何排序并不总是显而易见的。一般来说,seaborn 分类绘图函数尝试从数据中推断类别的顺序。如果您的数据具有 pandas Categorical 数据类型,那么可以在那里设置类别的默认顺序。如果传递到分类轴的变量看起来是数值的,则级别将被排序。但是,默认情况下,数据仍然被视为分类的,并在分类轴上以序数位置绘制(具体来说,在 0、1、… 上),即使使用数字对其进行标记也是如此。

sns.catplot(data=tips.query("size != 3"), x="size", y="total_bill")
../_images/categorical_11_0.png

从 v0.13.0 开始,所有分类绘图函数都有一个 native_scale 参数,当您想要使用数字或日期时间数据进行分类分组而不更改底层数据属性时,可以将其设置为 True

sns.catplot(data=tips.query("size != 3"), x="size", y="total_bill", native_scale=True)
../_images/categorical_13_0.png

选择默认排序的另一个选择是采用类别在数据集中出现的级别。排序也可以在特定于图的基础上使用 order 参数进行控制。这在同一个图中绘制多个分类图时很重要,我们将在下面详细介绍。

sns.catplot(data=tips, x="smoker", y="tip", order=["No", "Yes"])
../_images/categorical_15_0.png

我们已经提到了“分类轴”的概念。在这些示例中,这始终对应于水平轴。但将分类变量放在垂直轴上通常很有帮助(特别是当类别名称比较长或类别较多时)。要做到这一点,请交换变量到轴的分配。

sns.catplot(data=tips, x="total_bill", y="day", hue="time", kind="swarm")
../_images/categorical_17_0.png

比较分布#

随着数据集大小的增长,分类散点图在提供有关每个类别内值分布的信息方面变得有限。当这种情况发生时,有几种方法可以概括分布信息,从而方便地在类别级别之间进行比较。

箱线图#

第一个是大家熟悉的 boxplot()。这种类型的图显示了分布的三个四分位数值以及极值。“须”延伸到位于上下四分位数 1.5 个 IQR 内的点,然后独立显示超出此范围的观测值。这意味着箱线图中的每个值都对应于数据中的实际观测值。

sns.catplot(data=tips, x="day", y="total_bill", kind="box")
../_images/categorical_19_0.png

在添加 hue 语义时,语义变量每个级别的方框会变得更窄,并在分类轴上进行偏移。

sns.catplot(data=tips, x="day", y="total_bill", hue="smoker", kind="box")
../_images/categorical_21_0.png

这种行为被称为“躲避”,它由 dodge 参数控制。默认情况下(从 v0.13.0 开始),元素只有在否则会重叠时才会躲避。

tips["weekend"] = tips["day"].isin(["Sat", "Sun"])
sns.catplot(data=tips, x="day", y="total_bill", hue="weekend", kind="box")
../_images/categorical_23_0.png

一个相关的函数 boxenplot() 绘制一个类似于箱线图的图,但经过优化以显示有关分布形状的更多信息。它最适合大型数据集。

diamonds = sns.load_dataset("diamonds")
sns.catplot(
    data=diamonds.sort_values("color"),
    x="color", y="price", kind="boxen",
)
../_images/categorical_25_0.png

小提琴图#

另一种方法是小提琴图,它将箱线图与 分布 教程中描述的核密度估计程序结合在一起。

sns.catplot(
    data=tips, x="total_bill", y="day", hue="sex", kind="violin",
)
../_images/categorical_27_0.png

这种方法使用核密度估计来提供对值分布的更丰富描述。此外,箱线图的四分位数和须值显示在小提琴内。缺点是,由于小提琴图使用 KDE,因此可能需要调整其他一些参数,这使得与简单的箱线图相比,复杂性增加了一些。

sns.catplot(
    data=tips, x="total_bill", y="day", hue="sex",
    kind="violin", bw_adjust=.5, cut=0,
)
../_images/categorical_29_0.png

还可以“拆分”小提琴,这可以更有效地利用空间。

sns.catplot(
    data=tips, x="day", y="total_bill", hue="sex",
    kind="violin", split=True,
)
../_images/categorical_31_0.png

最后,在小提琴内部绘制的图有多种选择,包括显示每个单独的观测值而不是汇总的箱线图值的方法。

sns.catplot(
    data=tips, x="day", y="total_bill", hue="sex",
    kind="violin", inner="stick", split=True, palette="pastel",
)
../_images/categorical_33_0.png

swarmplot()stripplot() 与箱线图或小提琴图结合起来,可以有效地显示每个观测值及其分布的摘要。

g = sns.catplot(data=tips, x="day", y="total_bill", kind="violin", inner=None)
sns.swarmplot(data=tips, x="day", y="total_bill", color="k", size=3, ax=g.ax)
../_images/categorical_35_0.png

估计中心趋势#

在其他应用中,您可能希望显示每个类别中值的中心趋势的估计,而不是显示每个类别内的分布。Seaborn 提供了两种主要方法来显示此信息。重要的是,这些函数的基本 API 与上面讨论的 API 相同。

条形图#

一种常见的实现此目标的图表类型是条形图。在 seaborn 中,barplot() 函数对完整数据集进行操作,并应用函数来获取估计值(默认情况下取平均值)。当每个类别中有多个观测值时,它还会使用自举法来计算估计值的置信区间,并使用误差线将其绘制出来。

titanic = sns.load_dataset("titanic")
sns.catplot(data=titanic, x="sex", y="survived", hue="class", kind="bar")
../_images/categorical_37_0.png

默认情况下,误差线显示 95% 置信区间,但(从 v0.12 版本开始),可以使用其他几种表示方法。

sns.catplot(data=titanic, x="age", y="deck", errorbar=("pi", 95), kind="bar")
../_images/categorical_39_0.png

条形图的特殊情况是,当您想显示每个类别的观测值数量,而不是计算第二个变量的统计量时。这类似于对分类变量(而不是定量变量)进行直方图。在 seaborn 中,使用 countplot() 函数可以轻松地做到这一点。

sns.catplot(data=titanic, x="deck", kind="count")
../_images/categorical_41_0.png

无论是 barplot() 还是 countplot(),都可以调用上面讨论的所有选项,以及每个函数详细文档中演示的其他选项。

sns.catplot(
    data=titanic, y="deck", hue="class", kind="count",
    palette="pastel", edgecolor=".6",
)
../_images/categorical_43_0.png

点图#

另一种可视化相同信息的方式是使用 pointplot() 函数。此函数同样使用另一轴上的高度来编码估计值,但它不是显示完整的条形,而是绘制了点估计和置信区间。此外,pointplot() 将来自相同 hue 类别的点连接起来。这使得很容易看到主关系如何随色相语义的变化而变化,因为您的眼睛非常善于发现斜率的差异。

sns.catplot(data=titanic, x="sex", y="survived", hue="class", kind="point")
../_images/categorical_45_0.png

虽然分类函数缺少关系函数的 style 语义,但改变标记和/或线型以及色相仍然是一个好主意,从而使图形尽可能易于访问,并在黑白环境下也能很好地再现。

sns.catplot(
    data=titanic, x="class", y="survived", hue="sex",
    palette={"male": "g", "female": "m"},
    markers=["^", "o"], linestyles=["-", "--"],
    kind="point"
)
../_images/categorical_47_0.png

显示更多维度#

relplot() 一样,catplot() 建立在 FacetGrid 之上,这意味着很容易添加分面变量来可视化更高维度的关系。

sns.catplot(
    data=tips, x="day", y="total_bill", hue="smoker",
    kind="swarm", col="time", aspect=.7,
)
../_images/categorical_49_0.png

要进一步自定义图表,可以使用返回的 FacetGrid 对象上的方法。

g = sns.catplot(
    data=titanic,
    x="fare", y="embark_town", row="class",
    kind="box", orient="h",
    sharex=False, margin_titles=True,
    height=1.5, aspect=4,
)
g.set(xlabel="Fare", ylabel="")
g.set_titles(row_template="{row_name} class")
for ax in g.axes.flat:
    ax.xaxis.set_major_formatter('${x:.0f}')
../_images/categorical_51_0.png