如何在Seaborn箱线图中编辑须、离群值、盖帽等属性

13
我使用Seaborn包创建了一个带有覆盖的stripplot的嵌套箱线图。我在stackoverflow上看到了关于如何编辑sns.boxplot生成的ax.artists,以编辑单个箱子所有箱子box属性的答案。
是否有一种类似的方法来编辑whisker、cap、flier等属性?目前,我必须手动编辑seaborn->categorical.py文件中_BoxPlotter()类的restyle_boxplot方法中的值,以从默认绘图获得所需的绘图:
默认绘图: Default Plot

期望的绘图结果: Desired Plot

以下是我的参考代码:

sns.set_style('whitegrid')

fig1, ax1 = plt.subplots()


ax1 = sns.boxplot(x="Facility", y="% Savings", hue="Analysis",
             data=totalSavings)

plt.setp(ax1.artists,fill=False) # <--- Current Artist functionality

ax1 = sns.stripplot(x="Facility", y="% Savings", hue="Analysis",
                    data=totalSavings, jitter=.05,edgecolor = 'gray',
                    split=True,linewidth = 0, size = 6,alpha = .6)

ax1.tick_params(axis='both', labelsize=13)
ax1.set_xticklabels(['Test 1','Test 2','Test 3','Test 4','Test 5'], rotation=90)
ax1.set_xlabel('')
ax1.set_ylabel('Percent Savings (%)', fontsize = 14)


handles, labels = ax1.get_legend_handles_labels()
legend1 = plt.legend(handles[0:3], ['A','B','C'],bbox_to_anchor=(1.05, 1), 
                     loc=2, borderaxespad=0.)
plt.setp(plt.gca().get_legend().get_texts(), fontsize='12') 
fig1.set_size_inches(10,7)
2个回答

25

编辑:请注意,此方法似乎不再适用于matplotlib版本>=3.5。有关最新答案,请参见@JohanC的答案

你需要编辑存储在ax.lines中的Line2D对象。

这是一个脚本,用于创建箱形图(基于这里的示例),然后编辑线条和艺术家以符合您的要求(即没有填充,所有线条和标记具有相同的颜色等)。

您还可以修复图例中的矩形补丁,但您需要使用ax.get_legend().get_patches()

我还在第二个Axes上绘制了原始箱形图,作为参考。

import matplotlib.pyplot as plt
import seaborn as sns
    
fig,(ax1,ax2) = plt.subplots(2)

sns.set_style("whitegrid")
tips = sns.load_dataset("tips")

sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set1", ax=ax1)
sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set1", ax=ax2)

for i,artist in enumerate(ax2.artists):
    # Set the linecolor on the artist to the facecolor, and set the facecolor to None
    col = artist.get_facecolor()
    artist.set_edgecolor(col)
    artist.set_facecolor('None')

    # Each box has 6 associated Line2D objects (to make the whiskers, fliers, etc.)
    # Loop over them here, and use the same colour as above
    for j in range(i*6,i*6+6):
        line = ax2.lines[j]
        line.set_color(col)
        line.set_mfc(col)
        line.set_mec(col)

# Also fix the legend
for legpatch in ax2.get_legend().get_patches():
    col = legpatch.get_facecolor()
    legpatch.set_edgecolor(col)
    legpatch.set_facecolor('None')

plt.show()

这里输入图像描述


它不起作用。 - Joe
3
@Joe 是的,看起来在 matplotlib v3.5 左右发生了一些变化。请查看 JohanC 的答案以获取最新版本。 - tmdavison

8

对于matplotlib 3.5版本,盒子的矩形不再存储在ax2.artists中,而是存储在ax2.patches中。由于subplot的背景也被存储为矩形块,因此需要过滤掉矩形块的列表。

以下代码进行了一些调整:

  • 计算一个箱线图所属行的确切数量,因为根据箱线图选项可能会有不同数量的行
  • 使用saturation=1; seaborn更喜欢将一些去饱和度的效果添加到较大的区域中,但用全饱和度会更容易看到线条
import matplotlib
import matplotlib.pyplot as plt
import seaborn as sns

fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(12, 5))

sns.set_style("whitegrid")
tips = sns.load_dataset("tips")

sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set1", ax=ax1)
sns.boxplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set1", saturation=1, ax=ax2)

box_patches = [patch for patch in ax2.patches if type(patch) == matplotlib.patches.PathPatch]
if len(box_patches) == 0:  # in matplotlib older than 3.5, the boxes are stored in ax2.artists
    box_patches = ax2.artists
num_patches = len(box_patches)
lines_per_boxplot = len(ax2.lines) // num_patches
for i, patch in enumerate(box_patches):
    # Set the linecolor on the patch to the facecolor, and set the facecolor to None
    col = patch.get_facecolor()
    patch.set_edgecolor(col)
    patch.set_facecolor('None')

    # Each box has associated Line2D objects (to make the whiskers, fliers, etc.)
    # Loop over them here, and use the same color as above
    for line in ax2.lines[i * lines_per_boxplot: (i + 1) * lines_per_boxplot]:
        line.set_color(col)
        line.set_mfc(col)  # facecolor of fliers
        line.set_mec(col)  # edgecolor of fliers

# Also fix the legend
for legpatch in ax2.legend_.get_patches():
    col = legpatch.get_facecolor()
    legpatch.set_edgecolor(col)
    legpatch.set_facecolor('None')
sns.despine(left=True)
plt.show()

sns.boxplot with lines


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接