在对数据进行任何分析或建模时,一个早期的步骤应该是了解变量的分布方式。 分布可视化技术可以为许多重要问题提供快速答案。观测值涵盖多大范围?他们的核心趋势是什么? 它们是否严重偏向一个方向?有证据表明是双峰的吗?是否存在显著的异常值?这些问题的答案是否因其他变量定义的子集而异?
distributions
模块包含多个函数,旨在回答此类问题。
轴级函数为 histplot()
、kdeplot()
、ecdfplot()
和rugplot()
。
它们在图形级displot()
、 jointplot()
和pairplot()
函数中组合在一起。
有几种不同的方法可以可视化分布,每种方法都有其相对的优点和缺点。 了解这些因素非常重要,这样就可以为特定目标选择最佳方法。
绘制单变量直方图
可视化分布最常用的方法是直方图。这是 displot()
中的默认方法,
它使用与 histplot()
相同的底层代码。
直方图是一种柱状图,其中表示数据变量的轴被分成一组离散的桶,每个桶内的观测值计数使用相应柱的高度表示:
import seaborn as sns
sns.set_theme()
# penguins = sns.load_dataset("penguins")
penguins = sns.load_dataset("penguins",data_home='seaborn-data',cache=True)
sns.displot(penguins, x="flipper_length_mm")
<seaborn.axisgrid.FacetGrid at 0x7fbab8866c00>
sns.utils.get_data_home()
'/home/jovyan/.cache/seaborn'
sns.displot(penguins, x="flipper_length_mm", binwidth=3)
<seaborn.axisgrid.FacetGrid at 0x7fbab7c809b0>
在其他情况下,指定条柱的数量可能更有意义,而不是它们的大小:
sns.displot(penguins, x="flipper_length_mm", bins=20)
<seaborn.axisgrid.FacetGrid at 0x7fbab7c6f740>
默认值失败的一个示例是变量采用相对较少的整数值。 在这种情况下,默认图格宽度可能太小,从而在分布中产生尴尬的间隙:
# tips = sns.load_dataset("tips")
tips = sns.load_dataset("tips",data_home='seaborn-data',cache=True)
sns.displot(tips, x="size")
<seaborn.axisgrid.FacetGrid at 0x7fbac0dd8200>
一种方法是通过将数组传递给 bins
:
sns.displot(tips, x="size", bins=[1, 2, 3, 4, 5, 6, 7])
<seaborn.axisgrid.FacetGrid at 0x7fbab74a84a0>
也可以通过设置discrete=True
来实现,该设置选择表示数据集中唯一值的条柱分隔符,
其条形图以其对应值为中心。
sns.displot(tips, x="size", discrete=True)
<seaborn.axisgrid.FacetGrid at 0x7fbab74a5e50>
还可以使用直方图的逻辑可视化分类变量的分布为分类变量自动设置离散条柱, 但稍微“缩小”条形以强调轴的分类性质也可能有所帮助:
sns.displot(tips, x="day", shrink=.8)
<seaborn.axisgrid.FacetGrid at 0x7fbab66503b0>
sns.displot(penguins, x="flipper_length_mm", hue="species")
<seaborn.axisgrid.FacetGrid at 0x7fbab67aad80>
默认情况下,不同的直方图相互“分层”,在某些情况下,它们可能难以区分。 一种选择是将直方图的视觉表示从条形图更改为“阶梯”图:
sns.displot(penguins, x="flipper_length_mm", hue="species", element="step")
<seaborn.axisgrid.FacetGrid at 0x7fbab680f920>
或者,它们可以“堆叠”或垂直移动,而不是分层每个条形。在此图中,完整直方图的轮廓将仅与单个变量的图匹配:
sns.displot(penguins, x="flipper_length_mm", hue="species", multiple="stack")
<seaborn.axisgrid.FacetGrid at 0x7fbab67f46e0>
堆叠直方图强调了变量之间的部分-整体关系,但它可能会掩盖其他特征(例如,很难确定 Adelie 分布的模式)。 另一种选择是“躲避”条形图,将其水平移动并减小其宽度。 这确保了没有重叠,并且杆在高度方面保持可比性。但只有当分类变量具有少量级别时,它才有效:
sns.displot(penguins, x="flipper_length_mm", hue="sex", multiple="dodge")
<seaborn.axisgrid.FacetGrid at 0x7fbab662f650>
因为 displot()
是一个图形级函数,并且绘制到 FacetGrid
上,
所以也可以通过将第二个变量赋值为 col
或 row
而不是(或添加) hue
来在单独的子图中绘制每个单独的分布。
这很好地代表了每个子集的分布,但这使得进行直接比较变得更加困难:
sns.displot(penguins, x="flipper_length_mm", col="sex")
<seaborn.axisgrid.FacetGrid at 0x7fbab6463e60>
sns.displot(penguins, x="flipper_length_mm", hue="species", stat="density")
<seaborn.axisgrid.FacetGrid at 0x7fbab678b830>
但是,默认情况下,归一化将应用于整个分布,因此这只会重新缩放条形的高度。
通过设置common_norm=False
,每个子集将独立归一化:
sns.displot(penguins, x="flipper_length_mm", hue="species", stat="density", common_norm=False)
<seaborn.axisgrid.FacetGrid at 0x7fbab641f740>
密度归一化对条形进行缩放,使其面积之和为 1。因此,密度轴无法直接解释。 另一种选择是将条形规范化为它们的高度总和为 1。 当变量是离散的时,这是最有意义的,但它是所有直方图的一个选项:
sns.displot(penguins, x="flipper_length_mm", hue="species", stat="probability")
<seaborn.axisgrid.FacetGrid at 0x7fbab6065e80>
sns.displot(penguins, x="flipper_length_mm", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab687c9b0>
sns.displot(penguins, x="flipper_length_mm", kind="kde", bw_adjust=.25)
<seaborn.axisgrid.FacetGrid at 0x7fbab5d3d610>
请注意,窄带宽如何使双峰更加明显,但曲线却不那么平滑。 相比之下,较大的带宽几乎完全掩盖了双模态:
sns.displot(penguins, x="flipper_length_mm", kind="kde", bw_adjust=2)
<seaborn.axisgrid.FacetGrid at 0x7fbab6601730>
sns.displot(penguins, x="flipper_length_mm", hue="species", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab666eb70>
在许多情况下,分层的 KDE 比分层的直方图更容易解释,因此它通常是比较任务的不错选择。 然而,许多用于解析多个发行版的相同选项也适用于 KDE:
sns.displot(penguins, x="flipper_length_mm", hue="species", kind="kde", multiple="stack")
<seaborn.axisgrid.FacetGrid at 0x7fbab5fb44d0>
请注意,默认情况下,堆积图是如何填充每条曲线之间的区域的。
也可以填充单个或分层密度的曲线,尽管默认的 alpha
值(不透明度)会有所不同,
以便更容易解析单个密度。
sns.displot(penguins, x="flipper_length_mm", hue="species", kind="kde", fill=True)
<seaborn.axisgrid.FacetGrid at 0x7fbab5d9aff0>
sns.displot(tips, x="total_bill", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab5b3f050>
使用cut
参数可以部分避免这种情况,该参数指定曲线在极端数据点之外应该延伸多远。
但这只影响曲线的绘制位置;密度估计仍然会在没有数据存在的范围内平滑,
导致它在分布的极端情况下人为地降低:
sns.displot(tips, x="total_bill", kind="kde", cut=0)
<seaborn.axisgrid.FacetGrid at 0x7fbab5b9e720>
KDE 方法对于离散数据或数据自然连续但特定值被过度表示时也失败了。 需要记住的重要一点是,KDE 将始终显示平滑的曲线,即使数据本身并不平滑。 例如,考虑以下钻石重量分布:
diamonds = sns.load_dataset("diamonds", data_home='seaborn-data',cache=True)
sns.displot(diamonds, x="carat", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab5caaff0>
虽然 KDE 表明在特定值周围存在峰值,但直方图揭示了一个更加锯齿状的分布:
sns.displot(diamonds, x="carat")
<seaborn.axisgrid.FacetGrid at 0x7fbab5abcfb0>
作为一种妥协,可以将这两种方法结合起来。
在直方图模式下,displot()
(与histplot()
一样)可以选择包括平滑的KDE曲线(注意kde =True
,而不是kind="kde"
):
sns.displot(diamonds, x="carat", kde=True)
<seaborn.axisgrid.FacetGrid at 0x7fbab5be0d10>
sns.displot(penguins, x="flipper_length_mm", kind="ecdf")
<seaborn.axisgrid.FacetGrid at 0x7fbab58a4c20>
ECDF图有两个关键优点。与直方图或 KDE 不同,它直接表示每个数据点。 这意味着无需考虑条柱大小或平滑参数。此外,由于曲线是单调递增的,因此非常适合比较多个分布:
sns.displot(penguins, x="flipper_length_mm", hue="species", kind="ecdf")
<seaborn.axisgrid.FacetGrid at 0x7fbab58317f0>
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm")
<seaborn.axisgrid.FacetGrid at 0x7fbab5b13e60>
二元直方图将数据装箱到平铺绘图的矩形内,
然后使用填充颜色(类似于heatmap()
)显示每个矩形内的观测值计数。
类似地,双变量 KDE 图使用二维高斯平滑 (x, y) 观测值。
然后,默认表示显示 2D 密度的等值线:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab54298e0>
分配一个hue
变量将使用不同的颜色绘制多个热图或轮廓集。
对于双变量直方图,只有在条件分布之间有最小的重叠时,这才会有效:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", hue="species")
<seaborn.axisgrid.FacetGrid at 0x7fbab5f72c00>
双变量 KDE 图的等值线方法更适合评估重叠,尽管等值线过多的图可能会很忙:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", hue="species", kind="kde")
<seaborn.axisgrid.FacetGrid at 0x7fbab5949e80>
与单变量图一样,图格大小或平滑带宽的选择将决定图对基础二元分布的表示程度。 相同的参数适用,可以通过传递一对值来针对每个变量进行调整:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", binwidth=(2, .5))
<seaborn.axisgrid.FacetGrid at 0x7fbab53d3f80>
为了帮助解释热图,请添加一个颜色条来显示计数和颜色强度之间的映射:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", binwidth=(2, .5), cbar=True)
<seaborn.axisgrid.FacetGrid at 0x7fbab541ae70>
二元密度等值线的含义就不那么简单了。因为密度不能直接解释,
所以等高线是按密度的等比例绘制的,这意味着每条曲线都显示了一个水平集,
使得密度的某个比例p低于它。p值间隔均匀,最低级别由thresh
参数控制,数量由levels
控制:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", kind="kde", thresh=.2, levels=4)
<seaborn.axisgrid.FacetGrid at 0x7fbab5e200e0>
levels
参数还接受值列表,以便进行更多控制:
sns.displot(penguins, x="bill_length_mm", y="bill_depth_mm", kind="kde", levels=[.01, .05, .1, .8])
<seaborn.axisgrid.FacetGrid at 0x7fbab5ebb260>
双变量直方图允许一个或两个变量是离散的。 绘制一个离散变量和一个连续变量提供了另一种比较条件单变量分布的方法:
sns.displot(diamonds, x="price", y="clarity", log_scale=(True, False))
<seaborn.axisgrid.FacetGrid at 0x7fbab7a2b3b0>
相比之下,绘制两个离散变量是一种简单的方法来显示观测值的交叉表:
sns.displot(diamonds, x="color", y="clarity")
<seaborn.axisgrid.FacetGrid at 0x7fbab60e4fb0>
sns.jointplot(data=penguins, x="bill_length_mm", y="bill_depth_mm")
<seaborn.axisgrid.JointGrid at 0x7fbab5eff830>
与displot()
类似,在jointplot()
中设置不同的kind="kde"
将改变使用kdeploy()
的联合和边缘图:
sns.jointplot(
data=penguins,
x="bill_length_mm", y="bill_depth_mm", hue="species",
kind="kde"
)
<seaborn.axisgrid.JointGrid at 0x7fbaaf635ee0>
jointplot()
是JointGrid
类的一个方便接口,直接使用时提供了更大的灵活性:
g = sns.JointGrid(data=penguins, x="bill_length_mm", y="bill_depth_mm")
g.plot_joint(sns.histplot)
g.plot_marginals(sns.boxplot)
<seaborn.axisgrid.JointGrid at 0x7fbaaf2b1130>
显示边际分布的一种不那么突兀的方法使用“地毯”图,
该图在图的边缘添加一个小刻度来表示每个单独的观测值。这是内置的displot()
:
sns.displot(
penguins, x="bill_length_mm", y="bill_depth_mm",
kind="kde", rug=True
)
<seaborn.axisgrid.FacetGrid at 0x7fbaaf7ab6e0>
轴级函数rugplot()
可用于在任何其他类型的图的侧面添加地毯:
sns.relplot(data=penguins, x="bill_length_mm", y="bill_depth_mm")
sns.rugplot(data=penguins, x="bill_length_mm", y="bill_depth_mm")
<Axes: xlabel='bill_length_mm', ylabel='bill_depth_mm'>
sns.pairplot(penguins)
<seaborn.axisgrid.PairGrid at 0x7fbaaf0e17f0>
与jointplot()
/JointGrid
一样,直接使用底层的PairGrid
将提供更多的灵活性,
只需要更多的类型:
g = sns.PairGrid(penguins)
g.map_upper(sns.histplot)
g.map_lower(sns.kdeplot, fill=True)
g.map_diag(sns.histplot, kde=True)
<seaborn.axisgrid.PairGrid at 0x7fbaaf4ec980>