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

如何在Seaborn箱线-小提琴组合图中为每个类别添加阈值垂直线?

为Seaborn拆分式箱线-小提琴组合图添加分类专属阈值垂直线

问题描述

使用Seaborn绘制拆分式箱线-小提琴组合图后,希望为每个category添加对应唯一阈值的垂直线(类似示例图中的黑色线段),尝试axvlines未成功,寻求实现方法。

现有代码

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

sns.set_style('white')
palette = 'Pastel1'
plt.figure(figsize=(20,50))
plt.style.use("seaborn-whitegrid")
ax = sns.violinplot(y="category", x="value", data=df, hue="value", dodge=False,
                    palette=palette,
                    scale="width", inner=None)
xlim = ax.get_xlim()
ylim = ax.get_ylim()
for violin in ax.collections:
    bbox = violin.get_paths()[0].get_extents()
    x0, y0, width, height = bbox.bounds
    violin.set_clip_path(plt.Rectangle((x0, y0), width, height / 2, transform=ax.transData))

ax.set_alpha(0.6)
sns.boxplot(y="category", x="value", data=df, saturation=1, showfliers=False,
            width=0.1, boxprops={'zorder': 4, 'facecolor': 'white'}, ax=ax)
old_len_collections = len(ax.collections)
sns.stripplot(y="category", x="value", data=df, hue="category", palette=palette, dodge=False, ax=ax)
for dots in ax.collections[old_len_collections:]:
    dots.set_offsets(dots.get_offsets() + np.array([0.12, 0]))
ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.legend_.remove()
plt.show()

解决方案

因为图表是横向布局(y="category"),每个类别在y轴上对应一个离散区间,直接用axvlines无法自动匹配对应类别的y范围,需要手动获取每个类别的y轴位置和高度,再绘制覆盖该类别的垂直线。

修改后的完整代码

import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

# 替换为你的真实类别-阈值映射,确保键与df中category列的值完全匹配
category_thresholds = {
    "类别A": 14,
    "类别B": 21,
    "类别C": 17,
    # 更多类别及阈值...
}

sns.set_style('white')
palette = 'Pastel1'
plt.figure(figsize=(20,50))
plt.style.use("seaborn-whitegrid")
ax = sns.violinplot(y="category", x="value", data=df, hue="value", dodge=False,
                    palette=palette,
                    scale="width", inner=None)
xlim = ax.get_xlim()
ylim = ax.get_ylim()
for violin in ax.collections:
    bbox = violin.get_paths()[0].get_extents()
    x0, y0, width, height = bbox.bounds
    violin.set_clip_path(plt.Rectangle((x0, y0), width, height / 2, transform=ax.transData))

ax.set_alpha(0.6)
sns.boxplot(y="category", x="value", data=df, saturation=1, showfliers=False,
            width=0.1, boxprops={'zorder': 4, 'facecolor': 'white'}, ax=ax)
old_len_collections = len(ax.collections)
sns.stripplot(y="category", x="value", data=df, hue="category", palette=palette, dodge=False, ax=ax)
for dots in ax.collections[old_len_collections:]:
    dots.set_offsets(dots.get_offsets() + np.array([0.12, 0]))

# --- 添加分类阈值垂直线核心代码 ---
y_ticks = ax.get_yticks()
y_labels = [label.get_text() for label in ax.get_yticklabels()]

for idx, category in enumerate(y_labels):
    if category not in category_thresholds:
        continue
    threshold = category_thresholds[category]
    # Seaborn默认每个类别在y轴上的高度为0.8,取中心位置加减0.4得到上下边界
    y_bottom = y_ticks[idx] - 0.4
    y_top = y_ticks[idx] + 0.4
    # 绘制垂直线,设置样式确保显示在图表上层
    ax.plot([threshold, threshold], [y_bottom, y_top], color='black', linewidth=2, zorder=5)

ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.legend_.remove()
plt.show()

关键说明

  • category_thresholds:必须替换为你自己的类别与阈值的对应关系,键名要和数据中category列的取值完全一致
  • 若图表中类别在y轴上的高度有调整,可修改0.4这个数值来匹配实际的上下边界
  • zorder=5确保阈值线显示在箱线图和散点图上方,避免被遮挡

内容的提问来源于stack exchange,提问作者Daniil Khlebnikov

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:07:08