python - Seaborn 箱线图
问题描述
我有一个多索引 Pandas 数据框,我想将其绘制为箱线图。这应该很容易做到,但我发现自己无法得到我想要的东西。数据如下所示:
hedges mask model_name hedges_std hedges_min \
period season
2021-2025 winter 0.864328 1.0 ensemble 0.301748 0.124708
spring 0.740410 1.0 ensemble 0.202963 0.049319
summer 0.526264 1.0 ensemble 0.105750 0.162856
fall 0.531141 1.0 ensemble 0.046278 0.388827
2025-2050 winter 1.715075 1.0 ensemble 0.373866 0.582819
spring 1.252963 1.0 ensemble 0.370402 0.408695
summer 0.854958 1.0 ensemble 0.076193 0.528038
fall 0.759645 1.0 ensemble 0.068928 0.498271
2050-2075 winter 2.981373 1.0 ensemble 0.928940 1.139801
spring 2.042320 1.0 ensemble 0.748642 0.716289
summer 1.299277 1.0 ensemble 0.092611 0.812979
fall 1.108852 1.0 ensemble 0.109014 0.653199
2021-2025 winter 0.864328 1.0 ensemble 0.301748 0.124708
spring 0.740410 1.0 ensemble 0.202963 0.049319
summer 0.526264 1.0 ensemble 0.105750 0.162856
fall 0.531141 1.0 ensemble 0.046278 0.388827
2025-2050 winter 1.715075 1.0 ensemble 0.373866 0.582819
spring 1.252963 1.0 ensemble 0.370402 0.408695
summer 0.854958 1.0 ensemble 0.076193 0.528038
fall 0.759645 1.0 ensemble 0.068928 0.498271
2050-2075 winter 2.981373 1.0 ensemble 0.928940 1.139801
spring 2.042320 1.0 ensemble 0.748642 0.716289
summer 1.299277 1.0 ensemble 0.092611 0.812979
fall 1.108852 1.0 ensemble 0.109014 0.653199
hedges_max model_scenario
period season
2021-2025 winter 1.760912 ssp245
spring 1.189956 ssp245
summer 0.662142 ssp245
fall 0.687793 ssp245
2025-2050 winter 2.423660 ssp245
spring 2.040903 ssp245
summer 1.055890 ssp245
fall 0.965831 ssp245
2050-2075 winter 5.179203 ssp245
spring 3.898118 ssp245
summer 1.536149 ssp245
fall 1.435503 ssp245
2021-2025 winter 1.760912 ssp585
spring 1.189956 ssp585
summer 0.662142 ssp585
fall 0.687793 ssp585
2025-2050 winter 2.423660 ssp585
spring 2.040903 ssp585
summer 1.055890 ssp585
fall 0.965831 ssp585
2050-2075 winter 5.179203 ssp585
spring 3.898118 ssp585
summer 1.536149 ssp585
fall 1.435503 ssp585
我想绘制数据,显示每个时期和季节的一个框,按场景以颜色分隔。每个框将由其平均值(对冲)、标准差(std)以及可能的最小和最大范围定义。这个想法是显示未来期间将如何改变估计的对冲分布。我尝试了各种组合:
sns.boxplot(data=df, x="season", y="hedges", hue="model_scenario")
我的错误Could not interpret input 'season'
与我显然必须以某种方式分组或拆分的多索引有关,但这就是我一直失败的地方。对如何绘制这些数据的建议表示赞赏。
解决方案
我假设您的目标是生成这样的图形:
由于您已经计算了盒子的箱线图统计信息,因此该函数sns.boxplot()
以及matplotlib.axes.Axes.boxplot()
来自 matplotlib(这是 seaborn 后端并在内部调用sns.boxplot()
)不再是您可以使用的函数。试图自己计算统计数据,因此这ax.boxplot()
不是要走的路。
在计算 boxplot-statisticsmatplotlib.axes.Axes.boxplot()
调用 [ matplotlib.axes.Axes.bxp()
]( https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bxp.html ) 之后,这也是您可以使用的函数。
该函数matplotlib.axes.Axes.boxplot()
采用具有此名称约定的字典:
- med:中位数(标量浮点数),
- q1:第一个四分位数(第 25 个百分位数)(标量浮点数),
- q3:第三个四分位数(第 75 个百分位数)(标量浮点数),
- whislo:下须线的下限(标量浮点数),
- whishi:上胡须的上限(标量浮点数),
只需稍作修改,我们就可以重命名或分类您 DataFrame 所需的列。但首先重置你的多索引。
# df is defined and the multiinde
df = df.rename({'hedges':'med', 'hedges_min':'whislo', 'hedges_max':'whishi'}, axis=1)
df['q1'] = df['med'] - df['hedges_std']
df['q3'] = df['med'] + df['hedges_std']
df['label'] = df.apply(lambda x: '('+ x['period'] +' , '+ x['season'] + ')', axis=1)
df = df[['med', 'whislo','whishi','q1','q3', 'label']] # this are the columns we need
>>> df.head(5)
med whislo whishi q1 q3 label
0 0.864328 0.124708 1.760912 0.562580 1.166076 (2021-2025 , winter)
1 0.740410 0.049319 1.189956 0.537447 0.943373 (2021-2025 , spring)
2 0.526264 0.162856 0.662142 0.420514 0.632014 (2021-2025 , summer)
3 0.531141 0.388827 0.687793 0.484863 0.577419 (2021-2025 , fall)
4 1.715075 0.582819 2.423660 1.341209 2.088941 (2025-2050 , winter)
我决定创建一个结合period
和的标签season
。每个标签出现两次,每个标签model_scenario
恰好出现一次。
这是我如何创建上图的代码。它并不完美,但它显示了它是如何工作的。某些部分与sns.boxplot()
.
from matplotlib import rcParams
import matplotlib.pyplot as plt
colors = ['lightblue', 'olive']
model_scenario = ["ssp245", "ssp585"]
fig, ax = plt.subplots(figsize=(9, 4))
ax.set_title('box plot')
x_tick_label = []
x_tick_position = []
for i, group in enumerate(data_to_plt.groupby('label')):
for j in range(group[1].shape[0]):
x_tick_label.append(group[0])
x_tick_position.append(i)
if j ==0:
p = i - 0.15
else:
p = i + 0.15
artist_dict = ax.bxp(
bxpstats=[group[1].drop('label', axis=1).iloc[j].to_dict()],
showfliers=False,
patch_artist=True,
positions=[p]
)
for box in artist_dict["boxes"]:
box.update(dict(facecolor=colors[j],
zorder=.9,
edgecolor='gray',
linewidth=rcParams["lines.linewidth"])
)
if i == 0:
rect = plt.Rectangle([0,0], 0, 0,
linewidth=0,
edgecolor='gray',
facecolor=colors[j],
label=model_scenario[j])
ax.add_patch(rect)
ax.legend(loc='center left', bbox_to_anchor=(1, 0.5))
plt.xticks(x_tick_position, x_tick_label, rotation = 90)
总结一下我在 matplotlib 上所做的事情:
- 我按
model_scenario
aka分组labels
- 我为图例生成标签
- 我使用
bxp()
- 我重写了x-ticks
推荐阅读
- rest - 如何从服务器到客户端获取 Keycloak client_id 和 client_secret
- java - 如何在 apache.poi 中为数据栏制作纯色
- java - 在 Groovy 脚本中使用 @Field 变量
- c - 使用 fopen/write 时的汉字问题
- javascript - 如何更改类组件中的状态?
- java - Spring State Machine - 使用 StateMachineModelConfigurer 时定义 machineId
- android - 实现 MVVM LiveData RxJava Dagger Databinding 的正确结构?
- shell - 执行 shell 命令直到成功
- visual-studio - 如何在 C++ 中创建关于 MFC 或 MFC ActiveX 控件的 COM 对象?
- awk - 在找到包含模式的行后,我想使用 sed 或 awk 附加符合条件的多行