Python Seaborn如何在填充箱线图上叠加带阴影线的箱线图
叠加带阴影线箱线图的颜色不一致问题修复
问题描述
需要在填充样式的基础箱线图上,叠加带阴影线(hatched)的第二层箱线图。现有代码运行后整体布局效果符合预期,但存在箱线帽线、中位线与箱线边框颜色不统一的问题。
- 放开第一个
exit()的注释可得到单层基础填充箱线图效果 - 放开第二个
exit()的注释可得到两层箱线图叠加、未调整样式的效果
经测试matplotlib没有原生提供箱线图叠加的专用接口,现有单组箱线图添加阴影线的方案仅覆盖单层箱线图场景,不适用于两层叠加的需求。

测试复现代码
import matplotlib import matplotlib.pyplot as plt import seaborn as sns fig, ax = plt.subplots(figsize=(2, 5)) tips = sns.load_dataset("tips") tips['total_bill_plus5'] = tips['total_bill'] + 5 # 绘制第一层基础填充箱线图 sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, fliersize=0, whis=0) plt.ylim(0, 50) #exit() # 绘制第二层待加阴影线的箱线图 sns.boxplot(x="day", y="total_bill_plus5", hue="smoker", data=tips, fliersize=0, whis=0) #exit() ax.get_legend().remove() box_patches = [patch for patch in ax.patches if type(patch) == matplotlib.patches.PathPatch] if len(box_patches) == 0: # 兼容matplotlib 3.5以前版本,该版本箱型块存储在ax.artists中 box_patches = ax.artists num_patches = len(box_patches) lines_per_boxplot = len(ax.lines) // num_patches col = (.5, .5, .5, 1) # 原有存在bug的样式设置逻辑 for i, patch in enumerate( box_patches[int(num_patches/2):] ): patch.set_edgecolor(col) patch.set_facecolor('None') # 每个箱线对应多个Line2D对象(须、帽线、中位线、离群点等) for line in ax.lines[i * lines_per_boxplot: (i + 1) * lines_per_boxplot]: line.set_color(col) line.set_mfc(col) # 离群点填充色 line.set_mec(col) # 离群点边框色 patch.set_hatch('////')
问题原因
原有样式遍历逻辑存在索引错误:两次调用boxplot后,轴内的箱型块(patch)、线元素(lines)都是按绘制顺序存储的,第一层基础箱线的元素排在前半段,第二层待加阴影线的箱线元素排在后半段。原循环遍历第二层箱型块时,线元素切片从索引0开始取,实际修改的是第一层基础箱线的线颜色,第二层箱线的帽线、中位线仍保留默认颜色,因此出现颜色不匹配的问题。
修复方案
调整线元素的切片起始位置,确保遍历的线元素和当前处理的第二层箱型块一一对应即可,修正后的样式设置代码如下:
second_layer_patch_start = int(num_patches / 2) second_layer_line_start = second_layer_patch_start * lines_per_boxplot for idx, patch in enumerate(box_patches[second_layer_patch_start:]): # 设置箱型块样式 patch.set_edgecolor(col) patch.set_facecolor('none') patch.set_hatch('////') # 对应设置当前箱型块关联的所有线元素样式 line_slice_start = second_layer_line_start + idx * lines_per_boxplot line_slice_end = second_layer_line_start + (idx + 1) * lines_per_boxplot for line in ax.lines[line_slice_start:line_slice_end]: line.set_color(col) line.set_mfc(col) line.set_mec(col)
运行修正后的代码,第二层带阴影线的箱线边框、须线、帽线、中位线颜色会完全统一,达到预期效果。
内容的提问来源于stack exchange,提问作者PierreL
相关产品推荐
相关产品推荐

