【问题标题】:Add aggregate of all data to boxplots将所有数据的聚合添加到箱线图中
【发布时间】:2022-01-13 07:49:31
【问题描述】:

我有一个包含不同领域分数的数据集。所以这可以被认为是一个带有domainscore 列的DataFrame。我想为每个域绘制箱线图。这很容易。使用 seaborn,它看起来像这样:

import seaborn as sns
data = {"domain": ["econ", "econ", "public_affairs", "culture", "communication", "public_affairs", "communication",  "culture", "public_affairs", "econ",  "culture", "econ", "communication"],
        "score": [0.25, 0.3, 0.5684, 0.198, 0.15, 0.486, 0.78, 0.84, 0.48, 0.81, 0.1, 0.23, 0.5]}
ax = sns.boxplot(x="score", y="domain", data=data)

结果如下图:

但是,我希望在 y 轴上添加另一个刻度,其中为 所有 分数绘制箱形图,无论其域如何,刻度标签为“全部”。如果这个新的“全部”箱线图可以用一条水平线与其他数据分开,以明确“全部”本身不是一个域,那将是完美的。

我在照片编辑器程序中混合了一些东西来说明我在寻找什么,所以它会是这样的。特别重要的是所有地块之间的共享轴。

我最好的尝试是以下,它看起来不像我在上面的示例中想要的。

import seaborn as sns
data = {"domain": ["econ", "econ", "public_affairs", "culture", "communication", "public_affairs", "communication",  "culture", "public_affairs", "econ",  "culture", "econ", "communication"],
        "score": [0.25, 0.3, 0.5684, 0.198, 0.15, 0.486, 0.78, 0.84, 0.48, 0.81, 0.1, 0.23, 0.5]}
fig, axes = plt.subplots(2, 1, sharex=True)

sns.boxplot(ax=axes[0], x="score", y="domain", data=data)
all_box = sns.boxplot(ax=axes[1], data=data["score"], orient="h")

【问题讨论】:

    标签: matplotlib plot seaborn data-visualization boxplot


    【解决方案1】:

    您可以使用gridspec_kw 设置图之间的比率(例如[1,4],因为一个子图的框数是其 4 倍)。子图之间的间距可以通过hspace 进行微调。 axes[0].set_yticklabels() 让你设置标签。

    import matplotlib.pyplot as plt
    import seaborn as sns
    
    data = {"domain": ["econ", "econ", "public_affairs", "culture", "communication", "public_affairs", "communication",  "culture", "public_affairs", "econ",  "culture", "econ", "communication"],
            "score": [0.25, 0.3, 0.5684, 0.198, 0.15, 0.486, 0.78, 0.84, 0.48, 0.81, 0.1, 0.23, 0.5]}
    fig, axes = plt.subplots(2, 1, sharex=True,
                             gridspec_kw={'height_ratios': [1, 4], 'hspace': 0})
    sns.set_style('white')
    sns.boxplot(ax=axes[0], data=data["score"], orient="h", color='0.6')
    axes[0].set_yticklabels(['All'])
    sns.boxplot(ax=axes[1], x="score", y="domain", palette='Set2', data=data)
    plt.tight_layout()
    plt.show()
    

    另一种方法是将数据与一个副本和一个到处都是"All" 的标签连接起来。对于 pandas 数据框,您可以使用 df.copy()pd.concat()。只需一个列表字典,您就可以简单地复制列表。

    这样所有盒子的厚度都完全相同。由于它只使用了一个ax,它更容易与其他子图组合。

    import matplotlib.pyplot as plt
    import seaborn as sns
    
    data = {"domain": ["econ", "econ", "public_affairs", "culture", "communication", "public_affairs", "communication",  "culture", "public_affairs", "econ",  "culture", "econ", "communication"],
            "score": [0.25, 0.3, 0.5684, 0.198, 0.15, 0.486, 0.78, 0.84, 0.48, 0.81, 0.1, 0.23, 0.5]}
    
    data_concatenated = {"domain": ['All'] * len(data["domain"]) + data["domain"],
                         "score": data["score"] * 2}
    
    sns.set_style('darkgrid')
    palette = ['yellow'] + list(plt.cm.Set2.colors)
    ax = sns.boxplot(x="score", y="domain", palette=palette, data=data_concatenated)
    ax.axhline(0.5, color='0.5', ls=':')
    plt.tight_layout()
    plt.show()
    

    这是另一个示例,使用 pandas 和 seaborn 的航班数据集。它展示了在不添加额外水平线的情况下使摘要突出的不同方法:

    import matplotlib.pyplot as plt
    import seaborn as sns
    import pandas as pd
    
    flights = sns.load_dataset('flights')
    flights_all = flights.copy()
    flights_all['year'] = 'All'
    
    sns.set_style('darkgrid')
    palette = ['crimson'] + sns.color_palette('crest', len(flights['year'].unique()))
    
    ax = sns.boxplot(x="passengers", y="year", palette=palette, orient='h', data=pd.concat([flights_all, flights]))
    ax.axhspan(-0.5, 0.5, color='0.85', zorder=-1)
    # ax.axhline(0.5, color='red', ls=':') # optional separator line
    # ax.get_yticklabels()[0].set_color('crimson')
    ax.get_yticklabels()[0].set_weight('bold')
    plt.tight_layout()
    plt.show()
    

    【讨论】:

    • 这太好了,谢谢!一个小问题:我发现在我的情况下,水平线和箱线图之间的空间非常小,所以它几乎看起来好像这条线是箱线图的一部分。有没有办法增加线和子图之间的垂直空间?
    • 如果您指的是第一个解决方案,您可以在那里执行例如axes[0].set_ylim([0.7, -0.7])。您还可以将子图之间的刺更改为更薄或更暗。对于只有一个子情节的情节,移动东西会困难得多。
    • 您可以更改摘要箱线图背后的背景,而不是一条线。例如。 ax.axhspan(-0.5, 0.5, color='0.8', zorder=-1)。或者您可以选择与其他颜色完全不同的框的颜色,让事情变得清晰而没有分隔线。
    • 感谢您的补充和想法,这很有帮助!
    • sns.violinplot(..., scale='width')sns.boxenplot(....)(箱线图的递归变体)也可能很有趣。
    猜你喜欢
    • 1970-01-01
    • 2021-02-03
    • 1970-01-01
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-09
    相关资源
    最近更新 更多