可视化分类数据#

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

在 seaborn 中,有几种不同的方式来可视化涉及分类数据的关系。类似于 relplot()scatterplot()lineplot() 之间的关系,有两种方式来制作这些图。有多种轴级别的函数用于以不同方式绘制分类数据,还有一个图形级别的接口 catplot(),它提供了对这些函数的统一的高级访问。

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

分类散点图:

  • stripplot`(使用 ``kind="strip"`();默认)

  • swarmplot() (使用 kind="swarm")

分类分布图:

  • boxplot`(使用 ``kind="box"`()

  • violinplot`(使用 ``kind="violin"`()

  • boxenplot() (使用 kind="boxen")

分类估计图:

  • pointplot`(使用 ``kind="point"`()

  • barplot`(使用 ``kind="bar"`()

  • countplot`(使用 ``kind="count"`()

这些系列使用不同的粒度级别来表示数据。在决定使用哪一个时,你需要考虑你想回答的问题。统一的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

比较发行版#

随着数据集规模的增大,分类散点图在提供每个类别内值分布信息方面的能力变得有限。当这种情况发生时,有几种方法可以总结分布信息,以便于在类别级别之间进行轻松比较。

箱线图#

首先是熟悉的 箱线图() 。这种图显示了分布的三个四分位数值以及极值。’须线’延伸到位于上下四分位数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

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

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 与上述讨论的函数相同。

条形图#

实现这一目标的常见图表类型是条形图。在 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