Python绘图:箱线图的理解与绘制

一、箱线图简介

如下图所示,箱线图(箱形图、盒须图)是一种基于5个统计量(上边界、上四分位数、中位数、下四分位数以及下边界)显示数据分布的标准化方法,其可以用来检测数据的异常值和数据分布的形状,以及数据集的离散程度。图中矩形框显示数据集的上下四分位数,而矩形框中延伸出的线段(触须)则用于显示其余数据的分布位置,剩下超过上下四分位间距的数据点则被视为“异常值”。

箱线图主要的组成元素及其具体含义如下所示:

  • 下四分位数Q1:将所有数据按照从小到大的顺序排在第25%的数;
  • 上四分位数Q3:将所有数据按照从小到大的顺序排在第75%的数;
  • 四分位间距(IQR, Interquartile Range):统计学中的一种数学表示方法(也称为四分差),为上四分位数与下四分位之间的距离,可表示为\(\Delta = Q3 - Q1\)
  • 上边界:除异常点以外数据中的最大值,可表示为\(Q3 + 1.5 \Delta\)
  • 下边界:除异常点以外数据中的最小值,可表示为\(Q1 - 1.5 \Delta\)
  • 异常值:小于下边界与大于上边界的值。

二、箱线图的绘制

本文给出基于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绘图待扩展阅读

posted @ 2023-08-23 15:58  人工智能技术栈  阅读(6005)  评论(0编辑  收藏  举报