Python箱线图/小提琴图单侧轮廓不显示问题排查
问题:Matplotlib小提琴图单侧轮廓缺失,箱线图显示正常
使用Matplotlib绘制组合图表(小提琴图+箱线图+抖动散点)时,其中一组的小提琴图单侧轮廓无法显示,但更换其他y变量时图表完整。无报错信息,怀疑是轴范围限制导致截断。
数据示例
| category | y-data | y data that worked |
|---|---|---|
| 2 | 70 | 67 |
| 2 | 37 | 44 |
| 1 | 15 | 34 |
| 1 | 43 | 59 |
| 2 | 39 | 51 |
| 2 | 38 | 55 |
| 1 | 20 | 29 |
| 1 | 3 | 24 |
原始代码(补全缺失变量)
import pandas as pd import numpy as np import matplotlib.pyplot as plt import scipy.stats as st # 补全原始代码中缺失的常量 BLACK = '#2e2e2e' GREY_DARK = '#5a5a5a' COLOR_SCALE = ['#8856a7', '#9ebcda'] POSITIONS = [0, 1] # 数据加载与预处理 df = pd.DataFrame({ 'Category_Split': [2,2,1,1,2,2,1,1], 'y': [70,37,15,43,39,38,20,3], 'y_data_worked': [67,44,34,59,51,55,29,24] }) psqi = df.dropna() psqi['y'] = pd.to_numeric(psqi['y'], errors='coerce') psqi["Category_Split"] = psqi["Category_Split"].replace({1: "Poor", 2: "Good"}) categories = ["Poor", "Good"] y_data = [psqi[psqi["Category_Split"] == category]['y'].values for category in categories] # 创建抖动x数据 jitter = 0.032 x_data = [np.array([i] * len(d)) for i, d in enumerate(y_data)] x_jittered = [x + st.t(df=6, scale=jitter).rvs(len(x)) for x in x_data] # 初始化画布 fig, ax = plt.subplots(figsize=(8,6)) # 绘制小提琴图 violins = ax.violinplot( y_data, positions=POSITIONS, widths=0.45, bw_method="silverman", showmeans=False, showmedians=False, showextrema=False ) # 自定义小提琴图样式 for pc in violins["bodies"]: pc.set_facecolor("none") pc.set_edgecolor(BLACK) pc.set_linewidth(1.4) pc.set_alpha(1) # 绘制箱线图 medianprops = dict( linewidth=4, color=GREY_DARK, solid_capstyle="butt" ) boxprops = dict( linewidth=2, color=GREY_DARK ) ax.boxplot( y_data, positions=POSITIONS, showfliers = False, showcaps = False, medianprops = medianprops, whiskerprops = boxprops, boxprops = boxprops ) # 绘制抖动散点 for x, y, color in zip(x_jittered, y_data, COLOR_SCALE): ax.scatter(x, y, s = 100, color=color, alpha=0.4) # 设置x轴标签 ax.set_xticks(POSITIONS) ax.set_xticklabels(categories) plt.show()
问题原因分析
- 轴范围截断:小提琴图的核密度估计(KDE)尾部延伸到了Matplotlib自动设置的y轴范围之外,导致单侧轮廓被截断。比如
Poor组有极小值3,KDE会向更低值延伸,超出默认y轴下限。 - 带宽参数不合适:
bw_method="silverman"对于小样本或含极值的数据,可能会让KDE的带宽过大,导致密度曲线延伸更远,超出轴范围。
解决方案
方案1:手动扩展y轴范围
在绘图代码末尾添加以下代码,确保覆盖KDE的全部范围:
# 获取所有y数据的极值,再扩展10%的余量 all_y = np.concatenate(y_data) y_min, y_max = all_y.min() * 0.9, all_y.max() * 1.1 ax.set_ylim(y_min, y_max)
方案2:调整小提琴图带宽
将bw_method改为"scott"(更保守的带宽估计),或设置固定带宽值,避免KDE过度延伸:
violins = ax.violinplot( y_data, positions=POSITIONS, widths=0.45, bw_method="scott", # 替换为scott方法 # 或设置固定带宽:bw_method=0.5 showmeans=False, showmedians=False, showextrema=False )
方案3:检查并处理极值(可选)
如果数据中的极值是异常值,可以考虑截断或移除,再重新绘图(需结合业务场景判断合理性)。
验证效果
应用方案1后,小提琴图的完整轮廓会显示出来,同时保证箱线图和散点图不受影响。
内容的提问来源于stack exchange,提问作者Nat Allen
相关产品推荐
相关产品推荐

