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

如何用Seaborn绘制双Y轴且同组并列的多组小提琴图

问题描述

我目前绘制了如下小提琴图:
原始小提琴图
由于短期弹性数值集中在-0.1左右,长期弹性数值集中在-0.5左右,图表可读性很差。我想要实现:

  • 添加第二个Y轴对应短期数据
  • X轴为3类风险组标签
  • 每个风险组对应短期、长期弹性两个并列的小提琴图(共6个,同组不重叠)

我用Seaborn写了代码,但同组的图重叠了:

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

np.random.seed(50)

# 生成模拟数据
data1 = pd.DataFrame(np.random.normal(loc=0, scale=1, size=1000), columns=['Value'])
data2 = pd.DataFrame(np.random.normal(loc=5, scale=0.1, size=100), columns=['Value'])
data3 = pd.DataFrame(np.random.normal(loc=1, scale=1, size=1000), columns=['Value'])
data4 = pd.DataFrame(np.random.normal(loc=1, scale=0.1, size=100), columns=['Value'])
data5 = pd.DataFrame(np.random.normal(loc=2, scale=1, size=1000), columns=['Value'])
data6 = pd.DataFrame(np.random.normal(loc=2, scale=0.1, size=100), columns=['Value'])

# 创建画布和轴
fig, ax1 = plt.subplots()

# 在ax1上绘制第一组小提琴图
sns.violinplot(data=[data1['Value'], data3['Value'], data5['Value']], ax=ax1, palette=['tab:blue', 'tab:orange', 'tab:green'])

# 设置左侧Y轴样式
ax1.set_ylabel('Data 1', color='tab:blue')
ax1.tick_params(axis='y', labelcolor='tab:blue')

# 创建共享X轴的第二个Y轴
ax2 = ax1.twinx()

# 在ax2上绘制第二组小提琴图
sns.violinplot(data=[data2['Value'], data4['Value'], data6['Value']], ax=ax2, palette=['tab:red', 'tab:purple', 'tab:brown'])

# 设置右侧Y轴样式
ax2.set_ylabel('Data 2', color='tab:red')
ax2.tick_params(axis='y', labelcolor='tab:red')

# 设置X轴刻度和标签
ax1.set_xticks([0, 1, 2])
ax1.set_xticklabels(['No Risk', 'Double Risk', 'Expenditure Risk'])

# 设置X轴标签和标题
ax1.set_xlabel('Risk Level')
ax1.set_title('Three Sets of Violin Plots with Different Y-Axes')

# 调整轴位置
ax2.set_position([0.13, 0.1, 0.775, 0.8])

plt.show()

重叠的Seaborn小提琴图

我之前用Matplotlib实现了并列效果,但不知道怎么结合Seaborn做到:

import matplotlib.pyplot as plt
import numpy as np

# 生成模拟数据
data1 = np.random.normal(loc=0, scale=1, size=1000)
data2 = np.random.normal(loc=0, scale=0.1, size=100)
data3 = np.random.normal(loc=1, scale=1, size=1000)
data4 = np.random.normal(loc=1, scale=0.1, size=100)

# 创建画布和轴
fig, ax1 = plt.subplots()

# 在ax1上绘制第一组小提琴图,指定位置和宽度
vp1 = ax1.violinplot([data1, data3], positions=[0, 1], widths=0.5)
vp1['bodies'][0].set_facecolor('tab:blue')
vp1['bodies'][1].set_facecolor('tab:blue')

# 设置左侧Y轴样式
ax1.set_ylabel('Data 1', color='tab:blue')
ax1.tick_params(axis='y', labelcolor='tab:blue')

# 创建共享X轴的第二个Y轴
ax2 = ax1.twinx()

# 在ax2上绘制第二组小提琴图,指定偏移位置和宽度
vp2 = ax2.violinplot([data2, data4], positions=[0.5, 1.5], widths=0.5)
vp2['bodies'][0].set_facecolor('tab:red')
vp2['bodies'][1].set_facecolor('tab:red')

# 设置右侧Y轴样式
ax2.set_ylabel('Data 2', color='tab:red')
ax2.tick_params(axis='y', labelcolor='tab:red')

# 设置X轴刻度和标签,对齐每组中间
ax1.set_xticks([0.25, 1.25])
ax1.set_xticklabels(['No Risk', 'Double Risk'])

# 设置X轴标签和标题
ax1.set_xlabel('Risk Level')
ax1.set_title('Two Sets of Violin Plots with Different Y-Axes')

# 调整轴位置
ax2.set_position([0.13, 0.1, 0.775, 0.8])

plt.show()

并列的Matplotlib小提琴图

请问如何结合Seaborn实现双Y轴下,同风险组的两个小提琴图并列展示?


解决方案

核心思路是给Seaborn的violinplot手动指定偏移的X轴位置,让同组的两个图错开排列,同时调整X轴刻度到每组的中间位置。具体实现如下:

完整代码示例

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

np.random.seed(50)

# 生成模拟数据:长期弹性(左Y轴)、短期弹性(右Y轴)
long_data = pd.DataFrame({
    'Value': np.concatenate([
        np.random.normal(loc=-0.5, scale=0.1, size=1000),
        np.random.normal(loc=-0.5, scale=0.15, size=1000),
        np.random.normal(loc=-0.5, scale=0.08, size=1000)
    ]),
    'Risk': ['No Risk']*1000 + ['Double Risk']*1000 + ['Expenditure Risk']*1000
})

short_data = pd.DataFrame({
    'Value': np.concatenate([
        np.random.normal(loc=-0.1, scale=0.02, size=100),
        np.random.normal(loc=-0.1, scale=0.03, size=100),
        np.random.normal(loc=-0.1, scale=0.015, size=100)
    ]),
    'Risk': ['No Risk']*100 + ['Double Risk']*100 + ['Expenditure Risk']*100
})

# 创建画布和第一轴(对应长期弹性)
fig, ax1 = plt.subplots(figsize=(10,6))

# 给长期弹性的每个风险组指定X位置:0,1,2
x_pos_long = [0, 1, 2]
# 绘制长期小提琴图,指定宽度避免重叠
sns.violinplot(
    x=x_pos_long,
    y=long_data['Value'].groupby(long_data['Risk']).apply(list),
    ax=ax1,
    width=0.35,
    palette=['tab:blue', 'tab:orange', 'tab:green']
)

# 设置左侧Y轴样式
ax1.set_ylabel('长期弹性', color='tab:blue')
ax1.tick_params(axis='y', labelcolor='tab:blue')

# 创建共享X轴的第二轴(对应短期弹性)
ax2 = ax1.twinx()

# 给短期弹性的每个风险组指定偏移后的X位置:0.4,1.4,2.4(偏移量大于宽度)
x_pos_short = [pos + 0.4 for pos in x_pos_long]
# 绘制短期小提琴图
sns.violinplot(
    x=x_pos_short,
    y=short_data['Value'].groupby(short_data['Risk']).apply(list),
    ax=ax2,
    width=0.35,
    palette=['tab:red', 'tab:purple', 'tab:brown']
)

# 设置右侧Y轴样式
ax2.set_ylabel('短期弹性', color='tab:red')
ax2.tick_params(axis='y', labelcolor='tab:red')

# 设置X轴刻度:放在每组两个图的中间位置,对齐风险组标签
ax1.set_xticks([pos + 0.2 for pos in x_pos_long])
ax1.set_xticklabels(['No Risk', 'Double Risk', 'Expenditure Risk'])

# 设置X轴标签和标题
ax1.set_xlabel('风险组')
ax1.set_title('双Y轴并列小提琴图:不同风险组的长期/短期弹性')

# 调整布局避免标签重叠
plt.tight_layout()
plt.show()

关键细节说明

  1. 位置偏移控制:通过手动指定x参数的数值,让同组的两个小提琴图错开,偏移量要大于图的宽度,确保不重叠
  2. 数据分组处理:用groupby按风险组聚合数据,Seaborn可直接接收列表形式的分组数据
  3. 双Y轴区分:左右Y轴分别对应长期/短期数据,用颜色区分增强可读性
  4. 刻度对齐:X轴刻度放在每组两个图的中间位置,保证标签与分组一一对应

内容的提问来源于stack exchange,提问作者heinn

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 21:59:53