为何Matplotlib 1.4.0与2.2.0对同一数据生成的箱线图存在差异?
你遇到的这个版本差异问题,核心是Matplotlib在不同版本中对whis参数的解析逻辑、以及百分位数的计算方式发生了关键变更,咱们一步步拆解清楚:
先明确你的数据和预期值
你的数据集是:
values = np.array([0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,49.33,0.00,0.00,25.33])
排序后为:[0,0,0,0,0,0,0,0,0,0,25.33,49.33]。按标准线性插值计算,95%百分位数应为:
第95%位置 = 12 * 0.95 = 11.4,对应第11个元素(25.33)和第12个元素(49.33)的插值:25.33 + (49.33-25.33)*0.4 = 36.13
版本差异的具体原因
1. Matplotlib 1.4.0(旧版本):参数解析逻辑错误
在Matplotlib 1.5.0之前,boxplot的whis参数不支持直接传入百分位数列表——这个功能是1.5.0版本才新增的!当时whis的默认行为是设置为四分位距(IQR)的倍数,所以你传入的[5.0,95.0]被当成了:
- 下限 = Q1 - 5*IQR
- 上限 = Q3 + 95*IQR
而你的数据中,Q1和Q3都是0,IQR=0,所以上下须会被直接拉伸到数据的最大值(49.33)和最小值(0),完全不符合你想要的百分位数边界预期。
2. Matplotlib 2.2.0(新版本):百分位数计算方法不同
Matplotlib 1.5+支持用whis=[5,95]直接指定百分位数,但它默认的百分位数计算方式是取最接近排名的原始数据值,不做线性插值:
你的数据共12个元素,95%位置是11.4,Matplotlib会取最接近的整数索引(第10位,对应值25.33),所以图里的上限显示为25左右,和你手动计算的线性插值结果不符。
哪个结果是正确的?
严格来说,两个版本都没有正确实现你想要的“基于线性插值的5/95百分位数”:
- 旧版本完全误解了你的参数意图,结果完全偏离预期;
- 新版本虽然识别了参数,但计算逻辑和标准线性插值不一致。
如果想要得到符合预期的36.13作为95%百分位数上限,可以手动计算百分位数后自定义箱线图的须和帽,示例代码如下:
import numpy as np import matplotlib.pyplot as plt values = np.array([0.00,0.00,0.00,0.00,0.00,0.00,0.00,0.00,49.33,0.00,0.00,25.33]) # 用线性插值计算5%和95%分位数 p5 = np.percentile(values, 5, interpolation='linear') p95 = np.percentile(values, 95, interpolation='linear') # 计算四分位数(箱线图的箱体边界) q1, q3 = np.percentile(values, [25, 75]) f, ax1 = plt.subplots() # 先绘制基础箱线图 box = ax1.boxplot(values, showfliers=False) # 手动调整下须和下帽的位置 box['whiskers'][0].set_ydata([p5, q1]) box['caps'][0].set_ydata([p5, p5]) # 手动调整上须和上帽的位置 box['whiskers'][1].set_ydata([q3, p95]) box['caps'][1].set_ydata([p95, p95]) plt.show()
内容的提问来源于stack exchange,提问作者PyLabour

