如何用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()

我之前用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()

请问如何结合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()
关键细节说明
- 位置偏移控制:通过手动指定
x参数的数值,让同组的两个小提琴图错开,偏移量要大于图的宽度,确保不重叠 - 数据分组处理:用
groupby按风险组聚合数据,Seaborn可直接接收列表形式的分组数据 - 双Y轴区分:左右Y轴分别对应长期/短期数据,用颜色区分增强可读性
- 刻度对齐:X轴刻度放在每组两个图的中间位置,保证标签与分组一一对应
内容的提问来源于stack exchange,提问作者heinn
相关产品推荐
相关产品推荐

