I have created a nested boxplot with an overlayed stripplot using the Seaborn package. I have seen answers on stackoverflow regarding how to edit box properties both for individual boxes and for all boxes using ax.artists generated by sns.boxplot.
Is there any way to edit whisker, cap, flier, etc. properties using a similar method? Currently I have to manually edit values in the restyle_boxplot
method of the _BoxPlotter()
class in the seaborn -> categorical.py file to get from the default plot to the desired plot:
Here is my code for reference:
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)
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
You need to edit the Line2D
objects, which are stored in ax.lines
.
Heres a script to create a boxplot (based on the example here), and then edit the lines and artists to the style in your question (i.e. no fill, all the lines and markers the same colours, etc.)
You can also fix the rectangle patches in the legend, but you need to use ax.get_legend().get_patches()
for that.
I’ve also plotted the original boxplot on a second Axes, as a reference.
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()
Method 2
For matplotlib 3.5 the rectangles for the boxes aren’t stored anymore in ax2.artists
, but in ax2.patches
. As the background of the subplot is also stored as a rectangular patch, the list of patches needs to be filtered.
The code below further makes a few adjustments:
- the exact number of lines belonging to one boxplot is counted, as depending on the boxplot options there can be a different number of lines
saturation=1
is used; seaborn prefers to add some desaturation to larger areas, but lines will be better visible with full saturation
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()
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0