首页 > 编程笔记 > Python笔记 阅读:1

Python绘制箱线图(附带实例)

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


图 1 箱线图结构

箱线图的主要组成部分说明如下:
【实例 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 箱线图和散点图的组合图

相关文章