数据可视化:直方图、箱线图、概率图(Q-Q图)
各位工程师朋友,咱们接着聊。上一章我们把数据清洗干净了,但光看一堆数字,你很难看出门道。数据可视化,说白了就是给数据「拍X光片」——让隐藏的分布特征、异常点和趋势,一眼就能揪出来。
我个人习惯,拿到任何测试数据,第一件事不是建模,而是画图。你想想看,一个直方图能告诉你数据是不是偏态,一个箱线图能暴露离群点,一个Q-Q图能判断它是不是正态分布。这些信息,比任何统计检验都直观。
4.1 直方图:看数据的「身材」
直方图是最基础的分布图。它把数据分成若干区间(bins),然后数每个区间里有多少个点。我在项目中遇到过,有人把bins设成默认值,结果把双峰分布画成了单峰,差点误导了整批产品的寿命判断。
避坑指南: 我曾经吃过这个亏。bins数量太少,会掩盖细节;太多,又全是噪声。一般建议用 sqrt(n) 或 sturges 规则来算。
import matplotlib.pyplot as plt
import seaborn as sns
import numpy as np
# 生成模拟的寿命测试数据(单位:小时)
np.random.seed(42)
data = np.random.weibull(1.5, 1000) * 1000
# Matplotlib 直方图
plt.figure(figsize=(10, 6))
plt.hist(data, bins=30, edgecolor='black', alpha=0.7, color='steelblue')
plt.title('寿命测试数据直方图 (Matplotlib)')
plt.xlabel('寿命 (小时)')
plt.ylabel('频数')
plt.grid(True, alpha=0.3)
plt.show()
# Seaborn 直方图(更美观,自带密度曲线)
plt.figure(figsize=(10, 6))
sns.histplot(data, bins=30, kde=True, color='coral', edgecolor='black')
plt.title('寿命测试数据直方图 (Seaborn + KDE)')
plt.xlabel('寿命 (小时)')
plt.ylabel('频数')
plt.show()
嗯,这里要注意:Seaborn的 kde=True 会叠加一条核密度估计曲线,帮你更平滑地看分布趋势。我个人偏爱这种方式,尤其当数据量不大时,KDE曲线比直方图更稳定。
4.2 箱线图:揪出「捣乱分子」
箱线图,也叫盒须图。它用五个统计量(最小值、下四分位数、中位数、上四分位数、最大值)来概括分布。但真正厉害的是——它能自动标记离群点。
我记得有一次做加速寿命试验,有个样本的寿命特别短。一开始以为是测试错误,结果箱线图把它标成了离群点。后来一查,是那个批次的材料有杂质。箱线图帮我省了一次召回事故。
# 生成多组对比数据(不同温度下的寿命)
np.random.seed(42)
temp_25 = np.random.normal(5000, 500, 100)
temp_85 = np.random.normal(3500, 400, 100)
temp_125 = np.random.normal(2000, 300, 100)
# 故意加一个离群点
temp_125 = np.append(temp_125, 8000)
data_box = [temp_25, temp_85, temp_125]
labels = ['25°C', '85°C', '125°C']
# Matplotlib 箱线图
plt.figure(figsize=(10, 6))
bp = plt.boxplot(data_box, labels=labels, patch_artist=True,
showmeans=True, meanprops=dict(marker='D', markerfacecolor='red'))
# 给箱子着色
colors = ['lightblue', 'lightgreen', 'lightcoral']
for patch, color in zip(bp['boxes'], colors):
patch.set_facecolor(color)
plt.title('不同温度下寿命数据的箱线图')
plt.ylabel('寿命 (小时)')
plt.grid(True, alpha=0.3)
plt.show()
# Seaborn 箱线图(更简洁)
plt.figure(figsize=(10, 6))
sns.boxplot(data=data_box, palette='Set2')
plt.xticks(ticks=[0, 1, 2], labels=labels)
plt.title('不同温度下寿命数据的箱线图 (Seaborn)')
plt.ylabel('寿命 (小时)')
plt.show()
whis 参数调整这个倍数。
4.3 Q-Q图:检验「正态性」的照妖镜
Q-Q图(分位数-分位数图)是检验数据是否服从某个理论分布(最常见的是正态分布)的利器。它的原理很简单:把数据的分位数和理论分布的分位数画在散点图上。如果点大致落在一条直线上,就说明数据符合该分布。
为什么这个重要?因为很多寿命预测模型(比如威布尔分布、对数正态分布)都假设数据服从特定分布。你想想看,如果数据本身就不正态,你用基于正态的模型去拟合,结果能准吗?
import scipy.stats as stats
# 生成正态数据
np.random.seed(42)
normal_data = np.random.normal(5000, 500, 200)
# 生成偏态数据(模拟早期失效)
skewed_data = np.random.weibull(0.8, 200) * 3000
# Matplotlib Q-Q图
fig, axes = plt.subplots(1, 2, figsize=(14, 6))
# 正态数据
stats.probplot(normal_data, dist='norm', plot=axes[0])
axes[0].set_title('正态数据的Q-Q图')
axes[0].grid(True, alpha=0.3)
# 偏态数据
stats.probplot(skewed_data, dist='norm', plot=axes[1])
axes[1].set_title('偏态数据的Q-Q图')
axes[1].grid(True, alpha=0.3)
plt.tight_layout()
plt.show()
# Seaborn 没有直接的Q-Q图,但可以用 statsmodels
import statsmodels.api as sm
plt.figure(figsize=(10, 6))
sm.qqplot(normal_data, line='45', fit=True)
plt.title('正态数据的Q-Q图 (statsmodels)')
plt.grid(True, alpha=0.3)
plt.show()
你看,左边的Q-Q图,点基本都贴在红线上,说明数据正态。右边的图,两端明显偏离直线——尤其是右端翘起来了,说明数据右偏,有长尾。这在寿命数据里很常见,意味着早期失效多。
4.4 三种图的对比与选择
这三种图各有各的用处。我整理了一个对比表,方便你快速决策:
| 图表类型 | 主要用途 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 直方图 | 看分布形状、集中趋势 | 直观、容易理解 | 受bins设置影响大 | 初步探索、汇报展示 |
| 箱线图 | 看离群点、多组对比 | 不受极端值影响、适合对比 | 丢失分布细节 | 异常检测、不同条件对比 |
| Q-Q图 | 检验分布假设 | 精确判断分布类型 | 需要一定统计知识 | 模型假设验证、分布拟合 |
我个人习惯的顺序是:先用直方图看个大概,再用箱线图找离群点,最后用Q-Q图确认分布假设。三步走,基本能把数据的脾气摸透。
4.5 实战建议:把可视化嵌入工作流
别把画图当成「做完分析再补的作业」。我建议你养成一个习惯:每次拿到新数据,先画这三张图。花不了5分钟,但能帮你避免很多坑。
- 直方图:检查数据是否双峰(可能混合了两种失效模式)
- 箱线图:标记离群点,追溯原因(是测试错误还是真实异常?)
- Q-Q图:确认分布假设,为后续建模选对工具
举个例子。有一次我分析一批LED灯的寿命数据,直方图显示右偏,箱线图发现几个离群点,Q-Q图确认它符合威布尔分布。于是我用威布尔模型做寿命预测,结果和实际退运数据高度吻合。如果当时偷懒没画图,直接用正态模型,预测寿命会偏大30%。
嗯,数据可视化就是这么实在。它不花哨,但能救命。
公众号:蓝海资料掘金营,微信deep3321