目录
Matplotlib 可视化大师系列博客总览
本系列旨在提供一份系统、全面、深入的 Matplotlib 学习指南。以下是博客列表:
- 基础篇:
plt.plot()- 绘制折线图的利刃 - 分布篇:
plt.scatter()- 探索变量关系的散点图 - 比较篇:
plt.bar()与plt.barh()- 清晰对比的柱状图 - 统计篇:
plt.hist()与plt.boxplot()- 洞察数据分布 - 占比篇:
plt.pie()- 展示组成部分的饼图 - 高级篇:
plt.imshow()- 绘制矩阵与图像的强大工具 - 专属篇: 绘制误差线 (
plt.errorbar())、等高线 (plt.contour()) 等特殊图表 - 综合篇: 在一张图中组合多种图表类型
Matplotlib 可视化大师系列(四):plt.hist() 与 plt.boxplot() - 洞察数据分布
理解数据的分布特征是数据分析的核心环节。直方图(Histogram)和箱线图(Boxplot)是探索数据分布最常用的两种可视化工具。Matplotlib 通过 plt.hist() 和 plt.boxplot() 提供了强大的实现。本文将深入解析这两个函数,帮助你掌握洞察数据分布的艺术。
一、 直方图与箱线图:它们是什么?何时使用?
直方图 (Histogram) 将连续数据划分为一系列区间(称为"bins"),并统计每个区间内数据点的数量(频数)。它展示了单个连续变量的分布形状、中心趋势和离散程度。
- 适用场景:
- 了解数据的分布形状(是否对称?是单峰还是多峰?)
- 检查数据是否近似服从某种分布(如正态分布)
- 发现数据的异常值或间隙
- 注意:直方图用于连续数据。
箱线图 (Boxplot / Box-and-Whisker Plot) 用一种标准化的方式显示数据分布的五个关键统计量:最小值、第一四分位数 (Q1)、中位数 (Q2)、第三四分位数 (Q3) 和最大值。它能有效地显示数据的中位数、离散度、偏度和异常值。
- 适用场景:
- 比较多个组或类别的分布
- 识别数据中的异常值
- 直观地比较数据的中位数和散布程度
核心区别:
- 直方图:展示整个分布的形状和密度。
- 箱线图:总结分布的关键统计量,特别适合比较。
二、 函数原型与核心参数
plt.hist(x, bins=None, ...) - 直方图
plt.hist(x, bins=None, range=None, density=False, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, **kwargs)
核心参数详解:
-
数据与分箱:
x: 输入数据,一维数组。bins: 最重要的参数,决定如何划分区间。- 整数: 指定区间的数量(如
bins=20)。 - 序列: 指定区间的边界(如
bins=[0, 10, 20, 50, 100])。 - 字符串: 使用特定算法(如
'auto','sturges','fd','doane','scott','rice','sqrt')。'auto'通常是一个很好的默认选择。
- 整数: 指定区间的数量(如
range: tuple(min, max),指定数据范围,超出范围的值将被忽略。默认是(x.min(), x.max())。
-
分布显示:
density: 如果True,y轴显示的是密度(频率 / (区间宽度 * 总数量))而不是频数,使得直方图下的总面积等于1。常用于拟合概率分布。cumulative: 如果True,绘制累积分布直方图。histtype: 直方图类型。'bar'(默认,传统条形),'barstacked'(堆叠),'step'(生成未填充的线图),'stepfilled'(生成填充的线图)。
-
样式:
color: 颜色。alpha: 透明度。rwidth: 柱子的相对宽度(相对于bin的宽度)。orientation:'vertical'(默认)或'horizontal'。
plt.boxplot(x, ...) - 箱线图
plt.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, **kwargs)
核心参数详解:
-
数据:
x: 输入数据。可以是一个一维数组(绘制一个箱体),也可以是一个序列或二维数组(每个元素/列绘制一个箱体,用于比较)。
-
箱体样式:
notch: 如果True,绘制一个"缺口"箱线图,缺口的宽度表示中位数的置信区间。可用于直观比较中位数的显著性(如果缺口不重叠,中位数可能显著不同)。sym: 指定异常值的标记符号(如'r+'表示红色加号)。如果为空字符串'',则隐藏异常值。vert: 如果True(默认),箱线图垂直绘制;如果False,则水平绘制。whis: 定义须线的范围。默认是1.5。- 须线: 从箱体(Q1和Q3)延伸出的线,其长度通常为
1.5 * IQR(四分位距,IQR = Q3 - Q1)。须线末端的数据点被认为是异常值。
- 须线: 从箱体(Q1和Q3)延伸出的线,其长度通常为
widths: 箱体的宽度。patch_artist: 如果False(默认),箱体是线;如果True,箱体是填充的多边形,可以使用facecolor等参数。meanline: 如果True,将均值显示为一条线;如果False(默认),显示为一个点。
-
位置:
positions: 指定每个箱体的位置。
三、 从入门到精通:代码示例
示例 1:基础直方图与不同参数效果
import matplotlib.pyplot as plt
import numpy as np
# 生成一些正态分布的随机数据
np.random.seed(42)
data = np.random.normal(100, 15, 1000) # 均值=100,标准差=15
fig, axes = plt.subplots(2, 2, figsize=(12, 10))
# 1. 默认直方图
axes[0, 0].hist(data)
axes[0, 0].set_title('Default Histogram (Frequency)')
axes[0, 0].set_ylabel('Frequency')
axes[0, 0].grid(True, linestyle='--', alpha=0.7)
# 2. 密度直方图 (density=True)
axes[0, 1].hist(data, bins=30, density=True, alpha=0.7, color='green', edgecolor='black')
axes[0, 1].set_title('Density Histogram (Area=1)')
axes[0, 1].set_ylabel('Density')
axes[0, 1].grid(True, linestyle='--', alpha=0.7)
# 3. 累积分布直方图 (cumulative=True)
axes[1, 0].hist(data, bins=30, cumulative=True, density=True, color='orange', histtype='step', linewidth=2, label='CDF')
axes[1, 0].set_title('Cumulative Distribution Function (CDF)')
axes[1, 0].set_ylabel('Cumulative Probability')
axes[1, 0].grid(True, linestyle='--', alpha=0.7)
axes[1, 0].legend()
# 4. 水平直方图 (orientation='horizontal')
axes[1, 1].hist(data, bins=30, orientation='horizontal', color='purple', alpha=0.7)
axes[1, 1].set_title('Horizontal Histogram')
axes[1, 1].set_xlabel('Frequency')
axes[1, 1].set_ylabel('Value')
axes[1, 1].grid(True, linestyle='--', alpha=0.7)
plt.tight_layout()
plt.show()
示例 2:基础箱线图与比较
# 生成三组不同的数据用于比较
np.random.seed(42)
data1 = np.random.normal(90, 10, 100)
data2 = np.random.normal(110, 20, 100)
data3 = np.random.normal(80, 5, 100)
# 将数据组合成一个列表
all_data = [data1, data2, data3]
labels = ['Group 1', 'Group 2', 'Group 3']
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(14, 6))
# 1. 默认垂直箱线图
box_plot = ax1.boxplot(all_data, labels=labels, patch_artist=True)
ax1.set_title('Standard Vertical Boxplot')
ax1.set_ylabel('Value')
ax1.grid(True, axis='y', linestyle='--', alpha=0.7)
# 2. 水平箱线图
box_plot_h = ax2.boxplot(all_data, labels=labels, vert=False, patch_artist=True)
ax2.set_title('Horizontal Boxplot')
ax2.set_xlabel('Value')
ax2.grid(True, axis='x', linestyle='--', alpha=0.7)
# 为箱体上色 (一个常用的美化技巧)
colors = ['lightblue', 'lightgreen', 'pink']
for patch, color in zip(box_plot['boxes'], colors):
patch.set_facecolor(color)
for patch, color in zip(box_plot_h['boxes'], colors):
patch.set_facecolor(color)
plt.tight_layout()
plt.show()
示例 3:解读箱线图与高级特性
# 创建一个带有异常值的数据集
np.random.seed(123)
data_normal = np.random.normal(100, 15, 200)
# 手动添加一些异常值
data_with_outliers = np.append(data_normal, [40, 180, 190])
fig, ax = plt.subplots(figsize=(8, 6))
# 绘制箱线图,并自定义异常值符号
boxprops = dict(linestyle='-', linewidth=2, color='darkblue', facecolor='lightcyan')
flierprops = dict(marker='o', markerfacecolor='red', markersize=8, linestyle='none', markeredgecolor='red')
medianprops = dict(linestyle='-', linewidth=2.5, color='firebrick')
meanpointprops = dict(marker='D', markeredgecolor='black', markerfacecolor='green')
bp = ax.boxplot(data_with_outliers,
patch_artist=True,
boxprops=boxprops,
flierprops=flierprops,
medianprops=medianprops,
meanline=False,
showmeans=True,
meanprops=meanpointprops)
ax.set_title('Boxplot with Custom Styling and Outliers')
ax.set_ylabel('Value')
ax.grid(True, linestyle='--', alpha=0.7)
# 添加文本注释解释箱线图的各部分
ax.text(1.15, np.percentile(data_with_outliers, 25), 'Q1 (25th %)', va='center', ha='left')
ax.text(1.15, np.median(data_with_outliers), 'Median (Q2, 50th %)', va='center', ha='left')
ax.text(1.15, np.percentile(data_with_outliers, 75), 'Q3 (75th %)', va='center', ha='left')
# 计算IQR和须线
q1, median, q3 = np.percentile(data_with_outliers, [25, 50, 75])
iqr = q3 - q1
lower_whisker = q1 - 1.5 * iqr
upper_whisker = q3 + 1.5 * iqr
ax.text(1.15, lower_whisker, 'Lower Whisker\n(Q1 - 1.5*IQR)', va='center', ha='left')
ax.text(1.15, upper_whisker, 'Upper Whisker\n(Q3 + 1.5*IQR)', va='center', ha='left')
ax.text(0.85, 40, 'Outlier', va='center', ha='right')
plt.tight_layout()
plt.show()
四、 最佳实践与常见陷阱
-
直方图最佳实践:
- 谨慎选择 bin 数量: bin 太少会掩盖细节,太多会产生太多噪声。从
'auto'开始,然后根据数据特性调整。 - 使用密度图: 如果你关心的是概率分布而不是绝对计数,使用
density=True。 - 叠加 KDE: 可以在直方图上叠加核密度估计(KDE)图来平滑分布(通常用 Seaborn 的
sns.histplot(..., kde=True)更方便)。 - 对齐 bins: 使用
align='left','mid'(默认), 或'right'来控制柱体相对于 bin 边缘的位置。
- 谨慎选择 bin 数量: bin 太少会掩盖细节,太多会产生太多噪声。从
-
箱线图最佳实践:
- 用于比较: 箱线图的真正威力在于并排比较多个分布。
- 解释给观众: 箱线图对不熟悉的观众可能难以理解,确保做好标注或提供图例。
- 自定义样式: 使用
patch_artist=True和相关的*props参数来创建更美观、更易读的箱线图。 - 考虑小提琴图: 对于多模态(多个峰)分布,小提琴图(Violin Plot)能提供更多信息,它是箱线图和核密度图的结合。
-
常见陷阱:
- 直方图的 bin 选择偏见: 不同的 bin 数量或边界可以呈现出完全不同的故事。始终尝试多种设置。
- 误读箱线图: 记住箱线图的"箱体"代表中间50%的数据,不是数据的全部范围。
- 忽略异常值: 箱线图明确标出了异常值,不要忽视它们。它们可能是数据输入错误,也可能是重要的发现。
五、 总结
plt.hist() 和 plt.boxplot() 是探索数据分布不可或缺的工具。
plt.hist(): 揭示整个分布的形状、中心和散布。关注密度和频率。- 核心参数:
bins,density,cumulative。
- 核心参数:
plt.boxplot(): 总结分布的五个关键统计量,高效识别异常值和比较多个分布。- 核心参数:
patch_artist,whis,notch, 以及各种*props用于样式控制。
- 核心参数:
掌握这两个函数,你就能从简单的数据描述(如均值、标准差)深入到理解数据的内在结构和特性,这是数据分析和机器学习模型构建的基础。在下一篇文章中,我们将探讨如何展示组成部分,使用 plt.pie() 来绘制饼图。
1737

被折叠的 条评论
为什么被折叠?



