如何在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
相关产品推荐
相关产品推荐

