将所有数据的聚合添加到箱线图中

Add aggregate of all data to boxplots

我有一个包含不同领域分数的数据集。所以这可以被认为是一个包含列 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")

您可以使用 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()