功能相似,函数相似
Seaborn的命名空间是扁平的,所有函数功能都可以直接访问。但代码本身是有层次结构的,通过不同方式实现相似可视化目标的函数会被划分到相同的模块。大部分文档是围绕这些模块展开的:比如关系型 (relational),分布型 (distributional)、类别型 (categorical)
例如,分布模块 (distributions module)定义的函数专门用来反映数据点的分布,常见的方法包括直方图:
1 | penguins = sns.load_dataset('penguins') |
类似的但不那么常见的包括核密度估计:
1 | sns.kdeplot(data=penguins, x='flipper_length_mm', hue='species', multiple='stack') |
同一个模块中的函数共用很多底层代码和参数,这些参数在其它模块可能并不存在 (比如上述例子中的multiple='stack'
)。这是为了在探索数据时能方便地切换不同可视化方案,因为不同的可视化方案往往可以进行优势互补
图水平函数与轴水平函数
除了不同的模块,seaborn函数还有一种交叉的分类方式:轴水平 (axes-level)和图水平 (figure-level)。上述例子展示的是轴水平的函数,将数据绘制在单个matplotlib.pyplot.Axes
对象上,作为函数的返回值
图水平的函数与此不同,它通过一个seaborn对象 (通常是FacetGrid
)与matplotlib进行交互,管理整张图片。每个模块有一个图水平函数,统一其各个轴水平函数。函数组织方式如下:
例如,displot()
是分布模块的图水平函数,默认绘制直方图,底层使用与histplot()
相同的代码:
1 | sns.displot(data=penguins, x='flipper_length_mm', hue='species', multiple='stack') |
绘制核密度图使用与kdeplot()
相同的代码,使用kind
参数进行选择:
1 | sns.displot(data=penguins, x='flipper_length_mm', hue='species', multiple='stack', kind='kde') |
图水平函数与轴水平函数绘制的结果十分相似,但仍有一些不同。图水平函数绘制的图例在图外,图的形状也有细微的差别
图水平函数最有用的特征是能够轻松地创建包含多个子图的图片。例如,可以把不同品种企鹅的数据分面绘制到不同的子图,而不是堆积在同一个图上:
1 | sns.displot(data=penguins, x='flipper_length_mm', hue='species', col='species') |
图水平函数包装了其轴水平函数并将类别特异的关键字参数 (比如直方图的分箱大小)传递到底层函数。这意味着图水平函数不失灵活,但仍有一个缺点:函数文档中并没有这些类别特异的关键字参数。函数的有些参数可能没那么容易找到,需要查看两个不同的文档才能理解如何实现一个特定目标
轴水平函数绘制独立的图像
轴水平函数可直接替代matplotlib函数。虽然函数自动添加坐标轴标签和图例,但并不改变任何其绘制的图像以外的东西。这意味着它们可以随心所欲地插入复杂的matplotlib图片中
轴水平函数在内部调用matplotlib.pyplot.gca()
,将图片绘制在当前活跃的轴上。轴水平函数也可以通过ax=
参数,指定每一个子图的位置
1 | f, axs = plt.subplots(1, 2, figsize=(8, 4), gridspec_kw={width_ratios=[4, 3]}) |
图水平函数拥有其所在的图
相较而言,图水平函数无法轻易地与其它图片进行组合。图水平函数本质上“拥有”其所在的图,包括图的初始化过程,所以不存在使用图水平函数在已有的轴上进行绘制。这一限制使得图水平函数能够实现诸如把图例画在外面等特征
然而,通过访问函数返回的对象中的matplotlib的轴,仍然能够在图水平函数的基础上添加元素:
1 | tips = sns.load_dataset('tips') |
对图水平函数绘制的图像进行修饰
图水平函数返回一个FacetGrid
实例,可以通过一些方法修改图像的属性。例如,可以使用一行代码修改坐标轴的标签:
1 | g = sns.relplot(data=penguins, x='flipper_length_mm', y='bill_length_mm', col='sex') |
虽然方便,但要注意,这种方法并不是matplotlib API的一部分,只能用于图水平的函数
指定图的大小
如果想要放大或缩小matplotlib的图像,可以设置整张图的宽度和高度,既可以在初始化图的时候设置 (使用matplotlib.pyplot.subplots()
的figsize
参数),也可以调用图对象的方法进行设置 (matplotlib.Figure.set_size_inches()
)。当使用seaborn轴水平函数时,上述规则是通用的:子图的大小由整张图的大小决定
使用图水平函数时,有一些关键的不同。首先,函数本身有控制图片大小的参数。其次,参数height
和aspect
控制图片大小的方式与matplotlib中的width
和height
不同 (在seaborn参数中,width = height * aspect
)。最重要的是,这些参数控制每个子图的大小而不是整个图的大小
先用matplotlib.pyplot.subplots()
默认参数绘制一个子图:
1 | f, ax = plt.subplots() |
包含多个子图的图片整体尺寸也是与单个子图一样的,但是每个子图的轴会被压缩以适应空间:
1 | f, ax = plt.subplots(1, 2, sharey=True) |
然而,由图水平函数创建的子图是正方形的。使用FacetGrid
直接创建一个空的子图,这个函数是relplot()
,displot()
的底层函数:
1 | g = sns.FacetGrid(penguins) |
如果添加多列,整个图会变宽,每个子图的形状大小都是一样的:
1 | g = sns.FacetGrid(penguins, col='sex') |
调整子图的形状大小时,不需要考虑图中一共多少行多少列:
1 | g = sns.FacetGrid(penguins, col='sex', height=3.5, aspect=0.75) |
图水平函数的相对优势
优点 | 缺点 |
---|---|
便于使用数据中的变量进行分面 | 很多参数没有在函数文档中出现 |
图例默认画在图外 | 不能插入到其它matplotlib图中 |
便于进行图水平的定制 | 与matplotlib的API不同 |
不同的图片大小参数 | 不同的图片大小参数 |
组合数据的多个视图
Seaborn中有两个重要的绘图函数不能按照上述分类体系进行划分。jointplot()
和pairplot()
利用不同模块中的多种图像类型在同一张图中展示数据的多个维度。这两个图水平函数默认绘制具有多个子图的图片,但它们用不同的对象管理图片:分别是JointGrid
和PairGrid
jointplot()
在绘制两个变量的相互关系或者联合分布的同时,通过添加位于边缘的轴展示两个变量各自的分布:
1 | sns.jointplot(data=penguins, x='flipper_length_mm', y='bill_length_mm', hue='species') |
pairplot()
也是类似的情况,它整合了上述两种视图,不再聚焦单个相互关系,而是将每一对变量的组合同时进行可视化:
1 | sns.pairplot(data=penguins, hue='species') |
这两个函数的底层代码都是前面提到过的轴水平函数 (scatterplot()
和kdeplot()
),因此也可以接受kind
参数来实现不同表现形式的切换:
1 | sns.jointplot(data=penguins, x='flipper_length_mm', y='bill_length_mm', hue='species', kind |