【问题标题】:Using Seaborn, how do I get all the elements from a pointplot to appear above the elements of a violoinplot?使用 Seaborn,如何让点图中的所有元素出现在 violoinplot 的元素上方?
【发布时间】:2015-11-23 17:41:06
【问题描述】:

使用 Seaborn 0.6.0,我试图在 violinplot 上覆盖 pointplot。我的问题是violinplot 的单个观察中的“棒”被绘制在pointplot 的标记之上,如下所示。

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)

仔细看这个图,绿色的误差线显示在小提琴棒的上方(看看星期六),但蓝色的误差线和蓝色和绿色的点绘制在小提琴棒的下方。

我尝试将zorder 的不同组合传递给这两个函数,但这并没有改善情节的外观。我可以做些什么来让点图中的所有元素出现在 violoinplot 的所有元素之上?

【问题讨论】:

    标签: python matplotlib seaborn z-order


    【解决方案1】:

    与 Diziet Asahi 的回答类似,但更直接一些。由于我们设置的是 zorder,所以我们不需要按照我们希望它们出现的顺序来绘制绘图,这样就省去了对艺术家进行排序的麻烦。我也在制作它,以便点图不会出现在图例中,它没有用处。

    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)
    

    【讨论】:

    • get_children 返回 Axes 绘制树中包含的所有艺术家,包括刻度、标签等(并非所有这些都参与 zorder 排序)但这可能会产生令人惊讶的副作用.
    • 啊,好的。使用 darkgrid 样式,至少所有网格线都表现良好。
    • pointplot 中没有“legend=False”关键字,非常遗憾。
    【解决方案2】:

    这有点骇人听闻,我相信其他人会有更好的解决方案。请注意,我已更改您的颜色以改善两个图之间的对比度

    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
    

    编辑:在@tcaswell 的评论之后,我还提出了另一种解决方案(创建 2 个轴,一个用于每种类型的绘图)。但是请注意,如果您要进行布线,则必须重新设计图例,因为它们最终会在本示例中叠加。

    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('')
    

    【讨论】:

    • 应该有一种方法可以询问从 seaborn 调用返回的对象它创建了哪些新艺术家,这样您就不必这样做set 逻辑。如果没有,我将被动地要求您将其报告为针对 seaborn 的错误;)
    • @tcaswell 不幸的是,这些 seaborn 调用返回了 Axes 实例,并且由于两个调用都作用于同一个 Axes,因此第二个调用将其艺术家添加到第一个调用中。我想一个更简单的方法是在第一个子图之上创建第二个子图(带有sharex和sharey),并使用该轴创建sns.pointplot。这样,情节和他们的艺术家都会很好地分开
    • @DizietAsahi:使用集合的想法就像一个魅力,但我很好奇:你知道是否有另一种更直接的方法来获取 seaborn 情节的孩子?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 2016-01-20
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多