Python绘制箱线图(附带实例)
箱线图(Box Plot)也称为盒须图或盒式图,是一种常用的统计图表,用于显示数值变量的分布情况(包括中位数、四分位数、离散程度等)和异常值的存在,如下图所示。

图 1 箱线图结构
箱线图的主要组成部分说明如下:
【实例 1】使用 Matplotlib 生成多个箱线图,并通过设置不同的参数来改变箱线图的外观和显示方式。输入如下代码:
首先,生成了一组随机数据,并在一个图形中绘制了6种不同类型的箱线图。这些类型包括基本的箱线图、带缺口的箱线图、更改异常值点的符号、不显示异常值点、水平箱线图以及更改箱须长度。
接着,创建了更多的虚拟数据,并在同一个坐标轴上绘制了多个箱线图,展示了如何在同一幅图中比较多组数据的分布情况。
输出的结果如下图所示:

图 2 箱线图1
【实例 2】绘制两个箱线图,分别展示不同样本数据的分布情况。其中,一个是矩形箱线图,另一个是有缺口的箱线图。输入如下代码:

图 3 箱线图2
【实例 3】生成并展示不同类型的箱线图和小提琴图。输入如下代码:
首先,使用 sns.boxplot() 函数绘制了常规的箱线图。接着,在箱线图的基础上使用 sns.stripplot() 函数添加了 jitter,以更好地展示数据分布的密度。然后,使用 sns.violinplot() 函数绘制了小提琴图。最后,对基本的箱线图进行了定制化处理,在图中添加了每组观测数量和中位数的标签。
输出的结果如下图所示:

图 4 箱线图与小提琴图
【实例 4】利用 mpg_ggplot2 数据集,通过绘制箱线图展示不同车辆类型的高速公路里程分布情况。输入如下代码:

图 5 箱线图3
【实例 5】绘制一个箱线图和散点图的组合图,展示不同车辆类型的高速公路里程分布情况,并根据汽缸数进行着色区分。输入如下代码:
接着,使用 stripplot() 函数绘制散点图,同样横轴表示车辆类型,纵轴表示高速公路里程,散点颜色为黑色,大小为 3,并添加了一些随机抖动以避免重叠。然后,通过循环在图上添加了垂直线以增强可读性。
最后,设置了图表的标题,并添加了汽缸数的图例。输出的结果如下图所示。

图 6 箱线图和散点图的组合图

图 1 箱线图结构
箱线图的主要组成部分说明如下:
- 箱体:箱体由两条水平线和一条垂直线组成。箱体的底边界表示数据的下四分位数(Q1),顶边界表示数据的上四分位数(Q3),箱体的中线表示数据的中位数(或称为第二四分位数)。
- 须线:从箱体延伸出两条线段,分别表示数据的最小值和最大值,也可以是在异常值之外的数据范围。
- 异常值点:在箱体外部的点表示数据中的异常值,即与其他观测值相比显著偏离的值。
【实例 1】使用 Matplotlib 生成多个箱线图,并通过设置不同的参数来改变箱线图的外观和显示方式。输入如下代码:
import matplotlib.pyplot as plt import numpy as np np.random.seed(19781101) # 固定随机数种子,以便结果可复现 # 生成随机数据 spread = np.random.rand(50) * 100 center = np.ones(25) * 50 flier_high = np.random.rand(10) * 100 + 100 flier_low = np.random.rand(10) * -100 data = np.concatenate((spread, center, flier_high, flier_low)) fig, axs = plt.subplots(2, 3) # 基本的箱线图 axs[0, 0].boxplot(data) axs[0, 0].set_title('basic plot') # 带缺口的箱线图 axs[0, 1].boxplot(data, notch=True) axs[0, 1].set_title('notched plot') # 更改异常值点的符号 axs[0, 2].boxplot(data, sym='gd') axs[0, 2].set_title('change outlier\npoint symbols') # 不显示异常值点 axs[1, 0].boxplot(data, showfliers=False) axs[1, 0].set_title("don't show\noutlier points") # 水平箱线图 axs[1, 1].boxplot(data, vert=False, sym='rs') axs[1, 1].set_title('horizontal boxes') # 更改箱须长度 axs[1, 2].boxplot(data, vert=False, sym='rs', whisk=0.75) axs[1, 2].set_title('change whisker length') fig.subplots_adjust(left=0.08, right=0.98, bottom=0.05, top=0.9, hspace=0.4, wspace=0.3) # 生成更多的虚拟数据 spread = np.random.rand(50) * 100 center = np.ones(25) * 40 flier_high = np.random.rand(10) * 100 + 100 flier_low = np.random.rand(10) * -100 d2 = np.concatenate((spread, center, flier_high, flier_low)) # 如果所有列的长度相同,则可以将数据组织为 2-D 数组。如果不同,则使用列表 # 使用列表更有效率,因为 boxplot 内部会将 2-D 数组转换为矢量列表 data = [data, d2, d2[:2]] # 在同一个坐标轴上绘制多个箱线图 fig, ax = plt.subplots() ax.boxplot(data) plt.show()上述代码演示了如何使用 Matplotlib 创建不同类型的箱线图。
首先,生成了一组随机数据,并在一个图形中绘制了6种不同类型的箱线图。这些类型包括基本的箱线图、带缺口的箱线图、更改异常值点的符号、不显示异常值点、水平箱线图以及更改箱须长度。
接着,创建了更多的虚拟数据,并在同一个坐标轴上绘制了多个箱线图,展示了如何在同一幅图中比较多组数据的分布情况。
输出的结果如下图所示:

图 2 箱线图1
【实例 2】绘制两个箱线图,分别展示不同样本数据的分布情况。其中,一个是矩形箱线图,另一个是有缺口的箱线图。输入如下代码:
import matplotlib.pyplot as plt import numpy as np # 随机测试数据 np.random.seed(19781101) # 固定随机数种子,以便结果可复现 all_data = [np.random.normal(0, std, size=100) for std in range(1, 4)] labels = ['x1', 'x2', 'x3'] # 创建图形和轴对象 fig, (ax1, ax2) = plt.subplots(nrows=1, ncols=2, figsize=(9, 4)) # 绘制矩形箱线图 bplot1 = ax1.boxplot(all_data, vert=True, # 垂直箱体对齐 patch_artist=True, # 用颜色填充 labels=labels) # 将用于标记 X 轴刻度 ax1.set_title('Rectangular box plot') # 绘制有缺口的箱线图 bplot2 = ax2.boxplot(all_data, notch=True, # 缺口形状 vert=True, # 垂直箱体对齐 patch_artist=True, # 用颜色填充 labels=labels) # 将用于标记 X 轴刻度 ax2.set_title('Notched box plot') # 使用颜色填充 colors = ['pink', 'lightblue', 'lightgreen'] for bplot in (bplot1, bplot2): for patch, color in zip(bplot['boxes'], colors): patch.set_facecolor(color) # 添加水平网格线 for ax in [ax1, ax2]: ax.yaxis.grid(True) ax.set_xlabel('Three separate samples') ax.set_ylabel('Observed values') plt.show()上述代码使用 boxplot() 函数生成了两个子图,分别为矩形箱线图和带缺口的箱线图,使用循环为每个箱体设置了不同的颜色,并显示了水平网格线和轴标签。输出的结果如下图所示:

图 3 箱线图2
【实例 3】生成并展示不同类型的箱线图和小提琴图。输入如下代码:
import matplotlib.pyplot as plt import numpy as np import seaborn as sns import pandas as pd # 构造数据集 a = pd.DataFrame({'group': np.repeat('A', 500), 'value': np.random.normal(10, 5, 500)}) b = pd.DataFrame({'group': np.repeat('B', 500), 'value': np.random.normal(13, 1.2, 500)}) c = pd.DataFrame({'group': np.repeat('B', 500), 'value': np.random.normal(18, 1.2, 500)}) d = pd.DataFrame({'group': np.repeat('C', 20), 'value': np.random.normal(25, 4, 20)}) e = pd.DataFrame({'group': np.repeat('D', 100), 'value': np.random.uniform(12, size=100)}) df = pd.concat([a, b, c, d, e]) # 合并数据框 sns.boxplot(x='group', y='value', data=df) # 常规箱线图 plt.show() # 添加 jitter 的箱线图 ax = sns.boxplot(x='group', y='value', data=df) ax = sns.stripplot(x='group', y='value', data=df, color="orange", jitter=0.2, size=2.5) plt.title("Boxplot with jitter", loc="left") # 添加标题 plt.show() # 绘制小提琴图 sns.violinplot(x='group', y='value', data=df) plt.title("Violin plot", loc="left") # 添加标题 plt.show() # 绘制基本的箱线图 sns.boxplot(x="group", y="value", data=df) # 计算每组观测数量和中位数以定位标签 medians = df.groupby(['group'])['value'].median().values nobs = df.groupby("group").size().values nlabs = [f"n:{n:.0f}" for n in nobs] # 添加到图形中 pos = range(len(nobs)) for tick, label in zip(pos, ax.get_xticklabels()): plt.text(pos[tick], medians[tick] + 0.4, nobs[tick], horizontalalignment='center', size='medium', color='w', weight='semibold') plt.title("Boxplot with number of observation", loc="left") # 添加标题 plt.show()上述代码使用 Seaborn 库创建了不同类型的箱线图和小提琴图,并对图形进行了一些定制化的处理。
首先,使用 sns.boxplot() 函数绘制了常规的箱线图。接着,在箱线图的基础上使用 sns.stripplot() 函数添加了 jitter,以更好地展示数据分布的密度。然后,使用 sns.violinplot() 函数绘制了小提琴图。最后,对基本的箱线图进行了定制化处理,在图中添加了每组观测数量和中位数的标签。
输出的结果如下图所示:

图 4 箱线图与小提琴图
【实例 4】利用 mpg_ggplot2 数据集,通过绘制箱线图展示不同车辆类型的高速公路里程分布情况。输入如下代码:
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df = pd.read_csv("D:/DingJB/PyData/mpg_ggplot2.csv") # 导入数据 # 绘制图表 plt.figure(figsize=(13, 10), dpi=80) sns.boxplot(x='class', y='hwy', data=df, notch=False) # 在箱线图中添加观测数量(可选) def add_n_obs(df, group_col, y): # 计算每个类别的中位数 medians_dict = {grp[0]: grp[1][y].median() for grp in df.groupby(group_col)} # 获取 X 轴标签 xticklabels = [x.get_text() for x in plt.gca().get_xticklabels()] # 计算每个类别的观测数量 n_obs = df.groupby(group_col)[y].size().values # 遍历每个类别,在箱线图上方添加观测数量信息 for (x, xticklabel), n_ob in zip(enumerate(xticklabels), n_obs): plt.text(x, medians_dict[xticklabel] * 1.01, f"#obs :{str(n_ob)}", horizontalalignment='center', fontdict={'size': 10}, color='white') # 调用函数添加观测数量信息 add_n_obs(df, group_col='class', y='hwy') # 图表修饰 plt.title('Highway Mileage by Vehicle Class', fontsize=22) plt.ylim(10, 40) plt.show()上述代码使用 Seaborn 的 boxplot() 函数绘制箱线图,横轴表示车辆类型,纵轴表示高速公路里程。代码中定义了一个函数 add_n_obs(),用于在箱线图上方添加每个类别的观测数量信息,然后调用该函数将观测数量信息添加到图中。最后设置了标题,并限制了 Y 轴的范围。输出的结果如下图所示。

图 5 箱线图3
【实例 5】绘制一个箱线图和散点图的组合图,展示不同车辆类型的高速公路里程分布情况,并根据汽缸数进行着色区分。输入如下代码:
import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df = pd.read_csv("D:/DingJB/PyData/mpg_ggplot2.csv") # 导入数据 plt.figure(figsize=(10, 6), dpi=80) # 绘制图表 # 绘制箱线图,并根据汽缸数进行着色 sns.boxplot(x='class', y='hwy', data=df, hue='cyl') # 绘制散点图 sns.stripplot(x='class', y='hwy', data=df, color='black', size=3, jitter=0.6) # 在图上添加垂直线 for i in range(len(df['class'].unique()) - 1): plt.vlines(i + 0.5, 10, 45, linestyles='solid', colors='gray', alpha=0.2) # 图表修饰 plt.title('Highway Mileage by Vehicle Class', fontsize=16) plt.legend(title='Cylinders') plt.show()上述代码使用 Seaborn 的 boxplot() 函数绘制箱线图,横轴表示车辆类型,纵轴表示高速公路里程,并根据汽缸数进行了着色。
接着,使用 stripplot() 函数绘制散点图,同样横轴表示车辆类型,纵轴表示高速公路里程,散点颜色为黑色,大小为 3,并添加了一些随机抖动以避免重叠。然后,通过循环在图上添加了垂直线以增强可读性。
最后,设置了图表的标题,并添加了汽缸数的图例。输出的结果如下图所示。

图 6 箱线图和散点图的组合图