How to get outline color instead of fill color in grouped violinplot

Question:

I am using seaborn.violinplot() to derive the differences between two groups. Here is my code:

import seaborn

seaborn.set(style="whitegrid")

tips = seaborn.load_dataset("tips")

seaborn.violinplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set2", dodge=True)

enter image description here

However, I don’t want the fill color in the violin, and would like to distinguish these two groups with the color of the edge, here is an example:

enter image description here

How can I achieve this?

Asked By: Knotnet

||

Answers:

Coloring the outline instead of filling

You could loop through the generated violins and set their edgecolor (which normally is dark grey) to their facecolor. You can do the same with the legend.

As seaborn prefers to saturate colors that will be used for areas, you can add saturate=1 in the call to sns.violinplot.

import matplotlib.pyplot as plt
import matplotlib
import seaborn as sns

sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
ax = sns.violinplot(x="day", y="total_bill", hue="smoker", data=tips, palette="Set2", dodge=True, saturation=1)
for collection in ax.collections:
    if isinstance(collection, matplotlib.collections.PolyCollection):
        collection.set_edgecolor(collection.get_facecolor())
        collection.set_facecolor('none')
for h in ax.legend_.legendHandles:
    if isinstance(h, matplotlib.patches.Rectangle):
        h.set_edgecolor(h.get_facecolor())
        h.set_facecolor('none')
        h.set_linewidth(1.5)
plt.show()

outline only sns.violinplot

Also changing the inner box

The following code supposes the inner option isn’t changed from its default 'box'. It is tested with the current seaborn version (0.12.2). The inner box is represented by two line elements: a long thin line indicating the position of the boxplot whisker, and a thicker, shorter line indicating the quartiles. In addition, there is a white point to indicate the median (represented by a PathCollection).

import matplotlib.pyplot as plt
import matplotlib
import seaborn as sns

sns.set(style="whitegrid")
tips = sns.load_dataset("tips")
ax = sns.violinplot(x="sex", y="tip", hue="day", data=tips, palette="bright", dodge=True, saturation=1)
colors = []
for collection in ax.collections:
    if isinstance(collection, matplotlib.collections.PolyCollection):
        colors.append(collection.get_facecolor())
        collection.set_edgecolor(colors[-1])
        collection.set_facecolor('none')
if len(ax.lines) == 2 * len(colors):  # suppose inner=='box'
    for lin1, lin2, color in zip(ax.lines[::2], ax.lines[1::2], colors):
        lin1.set_color(color)
        lin2.set_color(color)
for h in ax.legend_.legendHandles:
    if isinstance(h, matplotlib.patches.Rectangle):
        h.set_edgecolor(h.get_facecolor())
        h.set_facecolor('none')
        h.set_linewidth(1.5)
plt.show()

coloring outline and inner box of violinplot

Answered By: JohanC