How can I iterate for linecolor for the latest update for seaborn boxplot to match my palette

63 Views Asked by At

A new parameter was added, linecolor, in the latest seaborn update (v 0.13.0) for boxplot. sns.boxplot.

https://seaborn.pydata.org/generated/seaborn.boxplot.html

Currently, one can only pass a 'color' for the linecolor argument for sns.boxplot.

I want to know if there is way to match my palette which I'm passing as a dict for the linecolor parameter.

Here is what my boxplot looks like now (I've redacted some information for IP purposes): enter image description here

Here is the dict I'm passing:

palette = {'Day1': '#B4C7E7', 'Day2': 'dodgerblue', 'Day3': '#2F5597'}

Here is what I want my boxplot to look like: enter image description here

Disregard the differences in legend size and and the yticks, these parameters I have changed by myself and were easily made. As you can see in this plot, the linecolor is matching the palette, I did this using another image software but its obviously a cumbersome and tedious exercise. Obviously I want to automate this process.

I don't think I needed to provide more information in terms of providing the dataframe I'm using or how I am instantiating the call for the sns.boxplot for this question, thats really straight forward.

See above.Everything I have provided should be sufficient for the SO audience.

2

There are 2 best solutions below

0
JohanC On

sns.boxplot's linecolor only supports a single color, independent of the hue. It also doesn't seem to support linecolor='face'.

Provided you are working with the latest matplotlib and seaborn versions, you could loop through the generated boxplots and update the line colors.

The following code has been tested with matplotlib 3.8.2 and seaborn 0.13.1:

import matplotlib.pyplot as plt
import seaborn as sns

tips = sns.load_dataset("tips")

ax = sns.boxplot(data=tips, x="smoker", y="tip", hue="day",
                 hue_order=['Fri', 'Sat', 'Sun'],
                 palette={'Fri': '#B4C7E7', 'Sat': 'dodgerblue', 'Sun': '#2F5597'})
for boxplot in ax.containers:
    color = boxplot.boxes[0].get_facecolor()
    plt.setp(boxplot.boxes, edgecolor=color)
    plt.setp(boxplot.caps, color=color)
    plt.setp(boxplot.fliers, color=color, markeredgecolor=color)
    plt.setp(boxplot.means, color=color)
    plt.setp(boxplot.medians, color=color)
    plt.setp(boxplot.whiskers, color=color)
for handle in ax.legend_.legend_handles: # update the legend
    handle.set_edgecolor(handle.get_facecolor())
plt.tight_layout()
plt.show()

seaborn boxplot where the lines follow the boxes color

0
mwaskom On

It's a litle fussy, but in v0.13+ you could layer an unfilled boxplot over a filled boxplot:

tips = sns.load_dataset("tips")
spec = dict(data=tips, x="day", y="total_bill", hue="sex", gap=.1)
sns.boxplot(**spec, linewidth=0, showfliers=False, boxprops=dict(alpha=.5))
sns.boxplot(**spec, fill=False, legend=False)

enter image description here