>>> from env_helper import info; info()
页面更新时间: 2024-01-06 20:45:16
运行环境:
    Linux发行版本: Debian GNU/Linux 12 (bookworm)
    操作系统内核: Linux-6.1.0-16-amd64-x86_64-with-glibc2.36
    Python版本: 3.11.2

5.6. 可视化统计关系

统计分析是了解数据集中的变量如何相互关联以及这些关系如何依赖于其他变量的过程。可视化可以成为此过程的核心组成部分,因为当数据正确可视化时,人类视觉系统可以看到表明关系的趋势和模式。

在本教程中,我们将讨论三个 seaborn 函数。我们最常使用的是relplot()。这是一个图形级函数,用于使用两种常用方法可视化统计关系:散点图和折线图。relplot()FacetGrid 与以下两个轴级函数之一组合在一起:

  • scatterplot()(和 kind="scatter"默认值)

  • lineplot()(和 kind="line")

正如我们将看到的,这些函数可能非常有启发性,因为它们使用简单且易于理解的数据表示形式,但这些表示形式仍然可以表示复杂的数据集结构。它们可以这样做,因为它们绘制了二维图形,可以通过使用色调、大小和样式的语义映射最多三个附加变量来增强这些图形。

>>> import numpy as np
>>> import pandas as pd
>>> import matplotlib.pyplot as plt
>>> import seaborn as sns
>>> sns.set_theme(style="darkgrid")

5.6.1. 将变量与散点图相关联

散点图是统计可视化的支柱。它使用点云描绘了两个变量的联合分布,其中每个点表示数据集中的一个观测值。这种描述使眼睛能够推断出大量关于它们之间是否存在任何有意义的关系的信息。

有几种方法可以在 seaborn 中绘制散点图。当两个变量都是数值时,应该使用最基本的scatterplot()函数。在分类可视化教程中,我们将看到使用散点图可视化分类数据的专用工具。scatterplot()relplot()默认值(也可以通过设置kind="scatter"来强制设置):

>>> tips = sns.load_dataset("tips")
>>> sns.relplot(data=tips, x="total_bill", y="tip")
<seaborn.axisgrid.FacetGrid at 0x7f536262f510>
_images/sec06_statistical_4_1.png

虽然点是在二维中绘制的,但可以通过根据第三个变量对点进行着色来将另一个维度添加到图中。在 seaborn 中,这被称为使用“色调语义”,因为点的颜色获得了意义:

>>> sns.relplot(data=tips, x="total_bill", y="tip", hue="smoker")
<seaborn.axisgrid.FacetGrid at 0x7fe8895c47d0>
_images/sec06_statistical_6_1.png

为了强调类之间的差异,并提高可访问性,可以对每个类使用不同的标记样式:

>>> sns.relplot(
>>>     data=tips,
>>>     x="total_bill", y="tip", hue="smoker", style="smoker"
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe8814fe010>
_images/sec06_statistical_8_1.png

也可以通过独立更改每个点的色调和样式来表示四个变量。但这应该小心,因为眼睛对形状的敏感度远低于对颜色的敏感度:

>>> sns.relplot(
>>>     data=tips,
>>>     x="total_bill", y="tip", hue="smoker", style="time",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe8c5ff2950>
_images/sec06_statistical_10_1.png

在上面的示例中,色调语义是分类的,因此应用了默认的定性调色板。如果色相语义为数字(具体而言,如果可以将其转换为浮点),则默认着色将切换到顺序调色板:

>>> sns.relplot(
>>>     data=tips, x="total_bill", y="tip", hue="size",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe881114050>
_images/sec06_statistical_12_1.png

在这两种情况下,您都可以自定义调色板。有很多选择可以做到这一点。这里,我们使用cubehelix_palette()的字符串接口定制一个顺序调色板:

>>> sns.relplot(
>>>     data=tips,
>>>     x="total_bill", y="tip",
>>>     hue="size", palette="ch:r=-.5,l=.75"
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe8814278d0>
_images/sec06_statistical_14_1.png

第三种语义变量改变每个点的大小:

>>> sns.relplot(data=tips, x="total_bill", y="tip", size="size")
<seaborn.axisgrid.FacetGrid at 0x7fe881306510>
_images/sec06_statistical_16_1.png

matplotlib.pyplot.scatter()不同,变量的文字值不用于选取点的面积。相反,以数据单位表示的值范围被规范化为以面积单位为单位的范围。此范围可以定制:

>>> sns.relplot(
>>>     data=tips, x="total_bill", y="tip",
>>>     size="size", sizes=(15, 200)
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe88071be50>
_images/sec06_statistical_18_1.png

scatterplot()API 示例中显示了有关如何自定义如何使用不同语义来显示统计关系的更多示例。

5.6.2. 强调线图的连续性

散点图非常有效,但没有普遍最佳的可视化类型。相反,视觉表示应根据数据集的具体情况以及您尝试用绘图回答的问题进行调整。

对于某些数据集,您可能希望将一个变量的变化理解为时间的函数,或者将类似的连续变量理解为变化。在这种情况下,一个不错的选择是绘制线图。在seaborn中,这可以通过lineplot()函数直接完成,也可以通过设置kind="line"relplot()一起完成:

>>> dowjones = sns.load_dataset("dowjones",data_home='seaborn-data',cache=True)
>>> sns.relplot(data=dowjones, x="Date", y="Price", kind="line")
<seaborn.axisgrid.FacetGrid at 0x7fe880641810>
_images/sec06_statistical_20_1.png

聚合和表示不确定性

更复杂的数据集将对相同的x变量值进行多次测量。seaborn 中的默认行为是通过绘制平均值和围绕平均值的 95% 置信区间来聚合每x个值的多个测量值:

>>> fmri = sns.load_dataset("fmri",data_home='seaborn-data',cache=True)
>>> sns.relplot(data=fmri, x="timepoint", y="signal", kind="line")
<seaborn.axisgrid.FacetGrid at 0x7f5362e8b190>
_images/sec06_statistical_22_1.png

置信区间是使用引导计算的,这对于较大的数据集来说可能非常耗时。因此,可以禁用它们:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", errorbar=None,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe8806af310>
_images/sec06_statistical_24_1.png

另一个不错的选择,尤其是对于较大的数据,是通过绘制标准差而不是置信区间来表示每个时间点的分布分布:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", errorbar="sd",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe87a11dbd0>
_images/sec06_statistical_26_1.png

要完全关闭聚合,将estimator参数设置为None。当数据在每个点上有多个观察值时,可能会产生奇怪的效果。

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal",
>>>     estimator=None,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe87a176010>
_images/sec06_statistical_28_1.png

使用语义映射绘制数据子集

lineplot()函数具有与scatterplot()相同的灵活性:它可以通过修改绘图元素的色调、大小和样式来显示最多三个附加变量。它使用与scatterplot()相同的API,这意味着我们不需要停下来考虑控制matplotlib中线与点外观的参数。

lineplot()中使用语义还将决定如何聚合数据。例如,添加具有两层的色调语义,将绘图分割为两条线和错误带,为每条线和错误带上色,以表示它们对应于哪个数据子集。

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", hue="event",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe87a1e8210>
_images/sec06_statistical_30_1.png

默认情况下,向线图添加样式语义会更改线条中的破折号模式:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal",
>>>     hue="region", style="event",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe87a52b790>
_images/sec06_statistical_32_1.png

但是,您可以通过每个观测值中使用的标记来识别子集,可以与破折号一起识别子集,也可以代替破折号:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", hue="region", style="event",
>>>     dashes=False, markers=True,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe879f3acd0>
_images/sec06_statistical_34_1.png

与散点图一样,在使用多个语义制作线图时要小心。虽然有时提供信息,但它们也可能难以解析和解释。但是,即使您只检查一个附加变量的更改,更改线条的颜色和样式也很有用。这可以使绘图在打印为黑白或由色盲人员查看时更易于访问:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", hue="event", style="event",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe879fbbe50>
_images/sec06_statistical_36_1.png

在处理重复测量数据(即,具有多次采样的单位)时,还可以单独绘制每个采样单位,而无需通过语义来区分它们。这样可以避免图例混乱:

>>> sns.relplot(
>>>     data=fmri.query("event == 'stim'"), kind="line",
>>>     x="timepoint", y="signal", hue="region",
>>>     units="subject", estimator=None,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7fe879ee42d0>
_images/sec06_statistical_38_1.png

lineplot()中图例的默认颜色图和处理还取决于色调语义是分类还是数字:

>>> dots = sns.load_dataset("dots").query("align == 'dots'")
>>> sns.relplot(
>>>     data=dots, kind="line",
>>>     x="time", y="firing_rate",
>>>     hue="coherence", style="choice",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f53714207d0>
_images/sec06_statistical_40_1.png

可能会发生这样的情况,即使 hue 变量是数字,它也不能很好地用线性色阶表示。这就是这里的情况, hue 变量的级别是对数缩放的。你可以通过传递一个列表或字典来为每行提供特定的颜色值:

>>> palette = sns.cubehelix_palette(light=.8, n_colors=6)
>>> sns.relplot(
>>>     data=dots, kind="line",
>>>     x="time", y="firing_rate",
>>>     hue="coherence", style="choice", palette=palette,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f53691b9550>
_images/sec06_statistical_42_1.png

或者,您可以更改颜色图的规范化方式:

>>> from matplotlib.colors import LogNorm
>>> palette = sns.cubehelix_palette(light=.7, n_colors=6)
>>> sns.relplot(
>>>     data=dots.query("coherence > 0"), kind="line",
>>>     x="time", y="firing_rate",
>>>     hue="coherence", style="choice",
>>>     hue_norm=LogNorm(),
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5368de6250>
_images/sec06_statistical_44_1.png

第三个语义 size 改变了行的宽度:

>>> sns.relplot(
>>>     data=dots, kind="line",
>>>     x="time", y="firing_rate",
>>>     size="coherence", style="choice",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5368ec6810>
_images/sec06_statistical_46_1.png

虽然size变量通常是数值,但也可以将分类变量与线条的宽度进行映射。这样做时要小心,因为除了“粗”线和“细”线之外,很难区分更多。但是,当线条具有高频变化时,破折号可能很难察觉,因此在这种情况下使用不同的宽度可能更有效:

>>> sns.relplot(
>>>     data=dots, kind="line",
>>>     x="time", y="firing_rate",
>>>     hue="coherence", size="choice", palette=palette,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5368f0dc10>
_images/sec06_statistical_48_1.png

控制排序和方向

由于lineplot()假设您最经常尝试将y绘制为x的函数,因此默认行为是在绘制之前按x值对数据进行排序。但是,这可以被禁用:

>>> healthexp = sns.load_dataset("healthexp").sort_values("Year")
>>> sns.relplot(
>>>     data=healthexp, kind="line",
>>>     x="Spending_USD", y="Life_Expectancy", hue="Country",
>>>     sort=False
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f53681a3b90>
_images/sec06_statistical_50_1.png

也可以沿 y 轴排序(和聚合):

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>      x="signal", y="timepoint", hue="event",
>>>     orient="y",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f53625902d0>
_images/sec06_statistical_52_1.png

5.6.3. 显示与分面的多种关系

我们在本教程中强调,虽然这些函数可以同时显示多个语义变量,但这样做并不总是有效的。但是,当您确实想了解两个变量之间的关系如何依赖于多个其他变量时呢?

最好的方法可能是制作多个图。因为relplot()是基于FacetGrid 的,这很容易做到。要显示附加变量的影响,与其将其分配给图中的语义角色之一,不如使用它来“分面”可视化。这意味着您可以创建多个轴,并在每个轴上绘制数据子集:

>>> sns.relplot(
>>>     data=tips,
>>>     x="total_bill", y="tip", hue="smoker", col="time",
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5362474290>
_images/sec06_statistical_54_1.png

还可以通过这种方式显示两个变量的影响:一个通过对列进行分面,另一个通过对行进行分面。当您开始向网格添加更多变量时,您可能希望减小图形大小。请记住,FacetGrid的大小是由每个小平面的高度和纵横比参数化:

>>> sns.relplot(
>>>     data=fmri, kind="line",
>>>     x="timepoint", y="signal", hue="subject",
>>>     col="region", row="event", height=3,
>>>     estimator=None
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5362602e90>
_images/sec06_statistical_56_1.png

当您想要检查变量的多个级别的效果时,最好在列上对该变量进行分面,然后将分面“包装”到行中:

>>> sns.relplot(
>>>     data=fmri.query("region == 'frontal'"), kind="line",
>>>     x="timepoint", y="signal", hue="event", style="event",
>>>     col="subject", col_wrap=5,
>>>     height=3, aspect=.75, linewidth=2.5,
>>> )
<seaborn.axisgrid.FacetGrid at 0x7f5362454d90>
_images/sec06_statistical_58_1.png

这些可视化有时被称为“点阵图”或“小倍数图”,它们非常有效,因为它们以一种格式呈现数据,使眼睛很容易发现总体模式和偏离这些模式的情况。虽然您应该利用scatterplot()relplot()提供的灵活性,但始终要记住,几个简单的图通常比一个复杂的图更有效。