Python绘图:小提琴图的理解与绘制

发布时间 2023-08-26 13:58:02作者: 人工智能技术栈

一、小提琴图简介

1.1 小提琴图的概念

小提琴图Violin Plot)通常用于显示数据的分布及其概率密度,其结合了箱形图和密度函数的特征,可以很好的显示数据的分布形状。如下图所示,中间的黑色粗线条表示四分位数的范围,从其上延伸出来的细黑线表示95%置信区间,而白色点则为中位数。

下面内容待定
箱形图在数据显示方面受到限制,简单的设计往往隐藏了有关数据分布的重要细节。例如使用箱形图时,我们不能了解数据分布是双模还是多模。虽然小提琴图可以显示更多详情,但它们也可能包含较多干扰信息。

小提琴图的内部是箱线图(有的图中位数会用白点表示,但归根结底都是箱线图的变化);外部包裹的就是核密度图,某区域图形面积越大,某个值附近分布的概率越大。

通过箱线图,可以查看有关数据的基本分布信息,例如中位数,平均值,四分位数,以及最大值和最小值,但不会显示数据在整个范围内的分布。如果数据的分布有多个峰值(也就是数据分布极其不均匀),那么箱线图就无法展现这一信息,这时候小提琴图的优势就展现出来了!

1.2 小提琴图与箱线图、核密度函数以及高斯混合体之间的关系与区别

核密度函数图:本质上是直方图的拟合曲线,其可以看作概率密度曲线。以正态分布的核密度函数为例,可结合博文? 如何通俗的理解正态分布 - 知乎来理解核密度函数图。
高斯混合体:一种多模态分布,即多个高斯分布的混合(即叠加),同样可以将其看作直方图的概率密度曲线。以高斯混合模型(Gaussian Mixture Models)为例子,可结合博文? 如何通俗的理解高斯混合模型(Gaussian Mixture Models) - 知乎来理解。
箱线图的理解可以参考? Python绘图:箱线图的理解与绘制 - 人工智能技术栈 - 博客园

下面我们分别绘制小提琴图箱线图关于核密度函数高斯混合体的结果图来理解他们之间的关系与区别。

二、箱线图的绘制

本文给出基于matplotlib与seaborn库的两种箱线图绘制方法。

2.1 基于matplotlib库的箱线图绘制

matplotlib中绘制箱线图的函数为boxplot(),其函数原型如下所示:

matplotlib.pyplot.boxplot(x, notch=None, sym=None, vert=None, whis=None, positions=None, widths=None, patch_artist=None, bootstrap=None, usermedians=None, conf_intervals=None, meanline=None, showmeans=None, showcaps=None, showbox=None, showfliers=None, boxprops=None, labels=None, flierprops=None, medianprops=None, meanprops=None, capprops=None, whiskerprops=None, manage_ticks=True, autorange=False, zorder=None, capwidths=None, *, data=None)

(1)函数主要参数及功能

常用的参数及功能如下表所示,为了方便理解各个参数的含义,本文将其划分为三个不同类别:常规参数显示控制参数以及细节属性参数

参数 功能 参数 功能
常规参数
x 指定要绘制箱形图的数据 notch 是否绘制带缺口的箱形图
patch_artist 是否填充箱体的颜色 vert 是否将箱线图垂直摆放
widths 指定箱线图的宽度 sym 指定异常点的形状
whis 指定上下边界与上下四分位数的距离 labels 为箱线图添加标签
capwidths 设置须线帽长度 position 指定箱线图的位置
显示控制参数
showmeans 是否显示均值点 meanline 是否显示均值线
showbox 是否显示箱形图的箱体 showcaps 是否显示须线帽
showfliers 是否显示异常值
细节属性参数
medianprops 设置中位线属性 meanprops 设置均值点属性
boxprops 设置箱体属性 capprops 设置须帽属性
whiskerprops 设置须线属性 flierprops 设置异常点属性

注意?:对于细节属性参数需要输入字典格式的数据,其设置方法可参考? python-matplotlib | 箱线图及解读 - 知乎

(2)函数返回值

boxplot()函数是以字典格式返回箱线图的每个组件对象,每个键的键值为matplotlib.lines.Line2D类对象的列表,具体包括:

  • boxes:箱体的主体显示四分位数;
  • medians:每个箱体的中位数水平线;
  • whiskers:每个箱体的须线;
  • caps:须线末端的直线,即须线帽;
  • fliers:异常值;
  • means:均值点或直线。

注意?:如果不启用箱线图的相应元素,相应键值则返回空列表。

(3)示例

更为详细的内容可参考matplotlib官方手册?:matplotlib.pyplot.boxplot — Matplotlib 3.7.2 documentation。这里选取matplotlib官网上一个具有代表性的箱线图示例作为演示对象(参考?:Box plots with custom fill colors — Matplotlib 3.7.2 documentation)。在官网示例的基础上,本文添加了相关参数的使用方法并对代码进行注释,代码如下所示:

import matplotlib.pyplot as plt
import numpy as np

#! 解决不显示的问题:中文设置为宋体格式
plt.rcParams['font.family'] = ["Times New Roman", 'SimSun']

# 生成一个包含三个随机数的数组,每个随机数包含100个数据,且他们的标准差分别为1,2,3
all_data = [np.random.normal(0, std, size=100) for std in range(1,4)]
labels = ['$x_1$', '$x_2$', '$x_3$']

fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 4))

# 绘制矩形箱线图
bplot_rect = ax1.boxplot(
    x=all_data,         # 需要绘制的数据
    vert=True,          # 垂直排列箱线图
    widths=0.3,         # 箱形宽度
    labels=labels,      # 箱形图的标签
    patch_artist=True,  # 是否为箱子填充颜色,默认为False
    medianprops={       # 设置中位线属性
        'linestyle': '-', 'color': 'r', 'linewidth': 1.5
    },
    # showmeans=True,     # 是否显示均值点,默认为False
    # meanline=True,      # 是否显示均值线,默认为False
    # meanprops={         # 设置均值点属性
    #     'marker': 'o', 'markersize': 7.5, 'markeredgewidth': 0.75, 'markerfacecolor': '#b7e1a1', 'markeredgecolor': 'r', 'color': 'k', 'linewidth': 1.5
    # },
    showfliers=True,    # 是否显示异常值,默认为True
    flierprops={        # 设置异常点属性
        'marker': '^', 'markersize': 6.75, 'markeredgewidth': 0.75, 'markerfacecolor': '#ee5500', 'markeredgecolor': 'k'
    },
    whiskerprops={      # 设置须的线条属性
        'linestyle': '--', 'linewidth': 1.2, 'color': '#480656'
    },
    capprops={
        'linestyle': '-', 'linewidth': 1.5, 'color': '#480656'
    }
)
title_rect = ax1.set_title("矩形箱形图")

# 绘制带缺口象形图
bplot_notch = ax2.boxplot(
    x=all_data,         # 需要绘制的数据
    notch=True,         # 是否带有缺口,默认False
    # vert=True,          # 垂直排列箱线图,默认为True
    widths=0.3,         # 箱形宽度
    labels=labels,      # 箱形图的标签
    patch_artist=True,  # 是否为箱子填充颜色,默认为False
    medianprops={       # 设置中位线属性
        'linestyle': '-', 'color': 'r', 'linewidth': 1.5
    },
    # showmeans=True,     # 是否显示均值点,默认为False
    # meanline=True,      # 是否显示均值线,默认为False
    # meanprops={         # 设置均值点属性
    #     'marker': 'o', 'markersize': 7.5, 'markeredgewidth': 0.75, 'markerfacecolor': '#b7e1a1', 'markeredgecolor': 'r', 'color': 'k', 'linewidth': 1.5
    # },
    showfliers=True,    # 是否显示异常值,默认为True
    flierprops={        # 设置异常点属性
        'marker': '^', 'markersize': 6.75, 'markeredgewidth': 0.75, 'markerfacecolor': '#ee5500', 'markeredgecolor': 'k'
    },
    whiskerprops={      # 设置须的线条属性
        'linestyle': '--', 'linewidth': 1.2, 'color': '#480656'
    },
    capprops={
        'linestyle': '-', 'linewidth': 1.5, 'color': '#480656'
    },
)
title_notch = ax2.set_title("带有缺口的箱形图")

# 为箱形图填充颜色
colors = ['pink', 'lightblue', 'lightgreen']
for bplot in (bplot_rect, bplot_notch):
    for patch, color in zip(bplot['boxes'], colors):
        patch.set_facecolor(color)

# 添加水平网格线并设置轴标签
for ax in [ax1, ax2]:
    ax.yaxis.grid(True)
    ax.set_xlabel("样本")
    ax.set_ylabel("观测值")

代码执行结果如下图所示

后续替换中文为宋体的图片

2.2 基于seaborn库的箱线图绘制

Seaborn是基于matplotlib的python数据可视化库。它提供了高层级的接口用于画出统计图。它与pandas库数据接口非常相近,可以直接使用pandas的数据结构。相比较于matplotlib的箱线图绘制,searborn绘制的更加美观。seaborn中绘制箱线图的函数为boxplot(),其函数原型如下所示:

seaborn.boxplot(data=None, *, x=None, y=None, hue=None, hue_order=None, orient=None, color=None, palette=None, saturation=0.75, width=0.8, dodge=True, fliersize=5, linewidth=None, whis=1.5, ax=None, **kwargs)

注意?:
(1)虽然seaborn的boxplot()能以arraylist以及DataFrame。但是其更适合于对DataFrame格式的数据进行箱线图绘制。因此,在绘制箱线图之前,建议将数据转换为DataFrame格式。本文的介绍以DataFrame格式的数据为例
(2)由于seaborn是基于matplotlib的,因此我们可以直接调用matplotlib.boxplot的参数对箱线图进行设置。

(1)函数主要参数功能及其返回值

常用的参数及功能如下表所示:

参数 功能 参数 功能
xy 数据或向量的变量名 data 用于绘图的数据集
width 箱体的宽度 linewidth 构成图元素的灰线宽度
orient 绘图方向,v(垂直)、h(水平) color 所有元素的颜色
fliersize 异常值标记的大小 notch 是否绘制带缺口的箱形图
whis 控制在超过高低四分位时IQR的比例 ax 使用的Axes轴对象,默认使用当前轴
palette 调色板名称 saturation 控制用于绘制颜色的原始饱和度比例
hue 指定色调的分组 dodge 使用色调嵌套时,元素是否沿分类轴移动
kwargs 可调用matplotlib.axes.Axes.boxplot参数,比如medianpropsboxprops

seaborn.boxplot()函数的返回值为当前绘制图像数matplotlib.Axes据格式的句柄对象

更为详细的内容可参考seaborn官方手册?:seaborn.boxplot — seaborn 0.12.2 documentation

(2)示例

下面通过若干个绘图示例来理解seaborn.boxplot参数的功能:

import seaborn as sns
import matplotlib.pyplot as plt

#! 解决不显示的问题:中文设置为宋体格式
plt.rcParams['font.family'] = ["Times New Roman", 'SimSun']

df = sns.load_dataset("titanic")
fig, axs = plt.subplots(nrows=2, ncols=3, figsize=(14,11))

sns.boxplot(ax=axs[0,0], data=df, x ="age", y="class")
axs[0,0].set_title("图1:舱位等级的年龄分布", fontsize=14)
axs[0,0].xaxis.grid(True)

# sns.boxplot(ax=axs[0,1], data=df, x="age", y="class", hue="alive")
sns.boxplot(ax=axs[0,1], data=df, x="class", y="age", hue="alive")  # 对掉x、y参数可以切换水平、垂直绘图
axs[0,1].set_ylabel('')
axs[0,1].set_title("图2:基于存活与否分组的舱位等级年龄分布", fontsize=14)
axs[0,1].yaxis.grid(True)

sns.boxplot(ax=axs[0,2], data=df[["age", "fare"]], orient="h")
axs[0,2].set_title("图3:绘制多列数值型数组的箱线图", fontsize=14)
axs[0,2].xaxis.grid(True)

sns.boxplot(ax=axs[1,0], data=df, x="fare", y="deck", hue="deck", dodge=False)
axs[1,0].set_title("图4:hue参数与dodge参数的作用", fontsize=14)
axs[1,0].xaxis.grid(True)

sns.boxplot(ax=axs[1,1], data=df, x="fare", y="deck", order=["G", "F", "E", "D", "C", "B", "A"], hue="deck", dodge=False)
axs[1,1].set_title("图5:改变图4中y轴的排列次序", fontsize=14)
axs[1,1].xaxis.grid(True)

sns.boxplot(
    ax=axs[1,2], data=df, x="age", y="class",
    notch=True, showcaps=False,
    flierprops={"marker": "^"},
    # boxprops={"facecolor": (.4, .6, .8, .5)},
    medianprops={"color": "r"}
)
axs[1,2].set_title("图6:使用matplotlib.boxplot参数进行配置", fontsize=14)
axs[1,2].xaxis.grid(True)

防采坑?:在使用df = sns.load_dataset("titanic")导入seaborn自带的数据时,由于网络原因会出现加载不了的问题。对此,可参考博文? 解决seaborn数据无法导入的问题_无法解析导入 seaborn_ryo007gnnu的博客-CSDN博客进行排雷。

附录

博文鉴赏:

Python绘图待扩展阅读