You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Python箱线图/小提琴图单侧轮廓不显示问题排查

问题:Matplotlib小提琴图单侧轮廓缺失,箱线图显示正常

使用Matplotlib绘制组合图表(小提琴图+箱线图+抖动散点)时,其中一组的小提琴图单侧轮廓无法显示,但更换其他y变量时图表完整。无报错信息,怀疑是轴范围限制导致截断。

数据示例

categoryy-datay data that worked
27067
23744
11534
14359
23951
23855
12029
1324

原始代码(补全缺失变量)

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()

问题原因分析

  1. 轴范围截断:小提琴图的核密度估计(KDE)尾部延伸到了Matplotlib自动设置的y轴范围之外,导致单侧轮廓被截断。比如Poor组有极小值3,KDE会向更低值延伸,超出默认y轴下限。
  2. 带宽参数不合适: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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 21:07:29