You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Seaborn中实现已分组箱线图的双侧展示?

如何在Seaborn中实现已分组箱线图的双侧展示?

我完全理解你的需求——你不想把两个索引的箱线图上下分开展示,而是希望把它们并排放在同一子图里,形成双侧对比的效果,每个主类别(Info1的One/Two/Three/Four)下,左右分别对应Index1和Index2的分组箱线图(按Info2的A/B/C/D细分)。其实这个需求在Seaborn里完全可以实现,核心是先把数据整理成Seaborn更擅长处理的长格式,再配合分组参数调整就能达到效果,我来一步步给你说明:

首先修正原代码的小笔误

你原代码里把index2写成了intex2,这会导致变量未定义的错误,先把这个笔误修正,后面的代码才能正常运行~

关键步骤:把宽格式数据转成长格式

你原来的DataFrame是宽格式(有Index1和Index2两列),而Seaborn的可视化工具对长格式数据的分组支持更好。我们可以用pd.melt把宽格式转成长格式,这样就能把Index1和Index2统一成一个“索引类型”列,对应的值放在另一列里:

# 转成长格式DataFrame
df_long = df.melt(
    id_vars=['Info1', 'Info2'],  # 保留作为分组依据的列
    value_vars=['Index1', 'Index2'],  # 需要转换的宽格式列
    var_name='Index_Type',  # 新列名:标记是Index1还是Index2
    value_name='Value'  # 新列名:对应的值
)

转成之后,df_long就有四列:Info1、Info2、Index_Type(值为Index1/Index2)、Value(对应索引的数值),这样Seaborn就能轻松处理多层分组了。

实现方式1:分面双侧对比(简单直观)

这种方式用seaborn.catplot,把Index_Type作为分面的列参数,生成左右两个子图,分别对应Index1和Index2的分组箱线图,实现起来非常简单,而且对比效果清晰:

import numpy as np
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt

# 修正笔误,生成数据
np.random.seed(42)  # 设置随机种子,保证结果可复现
index1 = np.random.random((4,100,4))
index2 = np.random.random((4,100,4))/2.  # 修正变量名

info1 = np.zeros(shape=index1.shape,dtype='object')
info1[0,:,:] = 'One'
info1[1,:,:] = 'Two'
info1[2,:,:] = 'Three'
info1[3,:,:] = 'Four'

info2 = np.zeros(shape=index1.shape, dtype='object')
info2[:, :, 0] = 'A'
info2[:, :, 1] = 'B'
info2[:, :, 2] = 'C'
info2[:, :, 3] = 'D'

# 构建宽格式DataFrame
df = pd.DataFrame(
    columns=['Info1', 'Info2', 'Index1', 'Index2'],
    data=np.array(
        (info1.flatten(), info2.flatten(), index1.flatten(), index2.flatten())).T)

# 转成长格式
df_long = df.melt(
    id_vars=['Info1', 'Info2'],
    value_vars=['Index1', 'Index2'],
    var_name='Index_Type',
    value_name='Value'
)

# 分面展示双侧箱线图
g = sns.catplot(
    data=df_long,
    x='Info1',
    y='Value',
    hue='Info2',  # 按Info2细分分组
    col='Index_Type',  # 按索引类型分面(左右两个子图)
    kind='box',
    dodge=True,  # 让同一Info1下的Info2箱线图分开
    height=4,
    aspect=1.2,
    order=['One', 'Two', 'Three', 'Four'],  # 指定Info1的展示顺序
    hue_order=['A', 'B', 'C', 'D']  # 指定Info2的展示顺序
)
g.set_axis_labels('Info1', '数值')
g.fig.suptitle('Index1 vs Index2 - 分面对比', y=1.02)
plt.show()

实现方式2:同一子图内双侧展示(更贴近你的示例图)

如果你希望所有内容都放在同一个子图里,每个Info1类别下左右分别对应Index1和Index2的分组箱线图,可以通过组合Info1和Index_Type生成新的x轴分组来实现:

# 生成新的x轴分组:Info1 + 索引类型
df_long['X_Group'] = df_long['Info1'] + ' - ' + df_long['Index_Type']
# 自定义x轴顺序,确保每个Info1下先展示Index1再展示Index2
x_order = [
    'One - Index1', 'One - Index2',
    'Two - Index1', 'Two - Index2',
    'Three - Index1', 'Three - Index2',
    'Four - Index1', 'Four - Index2'
]

# 绘制同一子图内的双侧箱线图
fig, ax = plt.subplots(figsize=(14, 6))
sns.boxplot(
    data=df_long,
    x='X_Group',
    y='Value',
    hue='Info2',  # 按Info2细分分组
    dodge=True,
    order=x_order,
    hue_order=['A', 'B', 'C', 'D'],
    ax=ax
)
# 旋转x轴标签,避免重叠
ax.tick_params(axis='x', rotation=45)
# 调整图例位置,避免遮挡图表
ax.legend(bbox_to_anchor=(1.05, 1), loc='upper left')
ax.set_title('Index1 vs Index2 - 同一子图双侧对比')
ax.set_xlabel('Info1 - 索引类型')
plt.tight_layout()
plt.show()

这样生成的图就和你提供的示例图风格非常接近了,每个Info1类别下左右两个分组分别对应Index1和Index2,每个分组里又按Info2的四个类别细分展示。

最后补充

两种方式各有优劣:分面展示更清晰,同一子图展示更紧凑。你可以根据自己的需求选择~

备注:内容来源于stack exchange,提问作者ymmx

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 10:43:08