简体   繁体   中英

Using Seaborn, how do I get all the elements from a pointplot to appear above the elements of a violoinplot?

Using Seaborn 0.6.0, I am trying to overlay a pointplot on a violinplot . My problem is that the 'sticks' from the individual observations of the violinplot are plotted on top of the markers from pointplot as you can see below.

import seaborn as sns
import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, figsize=[12,8])
sns.violinplot(x="day", y="total_bill", hue="smoker", data=tips,
               split=True, inner='stick', ax=ax, palette=['white']*2)
sns.pointplot(x="day", y='total_bill', hue="smoker",
                   data=tips, dodge=0.3, ax=ax, join=False)

在此输入图像描述

Looking closely at this figure, it appears as the green errorbar is plotted above the violoin sticks (look at Saturday), but the blue error bars, and the blue and green dots are plotted underneath the violin sticks.

I have tried passing different combinations of zorder to both functions, but that did not ameliorate the plot appearance. Is there anything I can do to get all the elements from the pointplot to appear above all the elements of the violoinplot?

Similar to Diziet Asahi's answer, but a little bit more straightforward. Since we're setting the zorder, we don't need to draw the plots in the order we want them to appear, which saves the trouble of sorting the artists. I'm also making it so that the pointplot doesn't appear in the legend, where it is not useful.

import seaborn as sns
import matploltlib.pyplot as plt

tips = sns.load_dataset("tips")

ax = sns.pointplot(x="day", y='total_bill', hue="smoker",
              data=tips, dodge=0.3, join=False, palette=['white'])
plt.setp(ax.lines, zorder=100)
plt.setp(ax.collections, zorder=100, label="")

sns.violinplot(x="day", y="total_bill", hue="smoker", data=tips,
               split=True, inner='stick', ax=ax)

在此输入图像描述

This is a bit hack-ish, I'm sure someone else will have a better solution. Notice that I have changed your colors to improve the contrast between the two plots

tips = sns.load_dataset("tips")

fig, ax = plt.subplots(1, figsize=[12,8])
sns.violinplot(x="day", y="total_bill", hue="smoker", data=tips,
               split=True, inner='stick', ax=ax)
a = list(ax.get_children()) # gets the artists created by violinplot
sns.pointplot(x="day", y='total_bill', hue="smoker",
                   data=tips, dodge=0.3, ax=ax, join=False, palette=['white'])
b = list(ax.get_children()) # gets the artists created by violinplot+pointplot

# get only the artists in `b` that are not in `a`
c = set(b)-set(a)
for d in c:
    d.set_zorder(1000) # set a-order to a high value to be sure they are on top

在此输入图像描述

EDIT: following @tcaswell 's comment, I also propose this other solution (create 2 Axes, one for each type of plot). Notice however, that if you're going to route, you'll have to rework the legend, since they end up superimposed in the present example.

tips = sns.load_dataset("tips")

fig = plt.figure(figsize=[12,8])
ax1 = fig.add_subplot(111)
sns.violinplot(x="day", y="total_bill", hue="smoker", data=tips,
               split=True, inner='stick', ax=ax1)

ax2 = fig.add_subplot(111, frameon=False, sharex=ax1, sharey=ax1)
sns.pointplot(x="day", y='total_bill', hue="smoker",
                   data=tips, dodge=0.3, ax=ax2, join=False, palette=['white'])
ax2.set_xlabel('')
ax2.set_ylabel('')

在此输入图像描述

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM