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

如何在Matplotlib Pyplot中绘制不同组大小的分组柱状图?

问题:分组柱状图隐藏无数据的样本

我希望绘制不同组大小的分组柱状图,目前代码能实现分组,但部分组的样本(Run)数量不一致,出现了空样本显示的情况,想调整成只显示有数据的样本。

原代码如下:

from matplotlib import pyplot as plt
from matplotlib import transforms
import numpy as np
import pandas as pd
import seaborn as sns

df_2 = pd.DataFrame({'Machine Number':  ['Machine 1', 'Machine 1', 'Machine 2','Machine 3','Machine 4','Machine 4'],
             'Experiment': ['Run 1','Run 2','Run 1', 'Run 1','Run 1','Run 2'],
             'Result': [10,32,71, 11,13,7]
             })
machines = df_2['Machine Number'].unique()
experiments = df_2['Experiment'].unique()

fig, ax = plt.subplots(figsize=(12, 4))
ax = sns.barplot(x='Machine Number', y='Result', hue='Experiment', palette='Reds', data=df_2, ax=ax)

experiment_pos = np.sort( [p.get_x() + p.get_width()/2  for p in ax.patches])

ax.set_xticks(experiment_pos)
ax.set_xticklabels(np.tile(experiments, len(machines)), rotation=30)
ax.get_legend().remove()
ax.set_xlabel('') # remove default xlabel


machine_pos = experiment_pos.reshape(-1, len(experiments)).mean(axis=1)
trans = transforms.blended_transform_factory(ax.transData, ax.transAxes)

for pos, machine in zip(machine_pos, machines):
    ax.text(pos, -0.25,  machine, transform=trans, ha='center', va='bottom', color='steelblue', fontsize=14)
for pos in (machine_pos[:-1] + machine_pos[1:]) / 2:
    ax.axvline(pos, 0, -0.25, color='steelblue', ls=':' , clip_on=False)
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()

# st.pyplot(fig)         

解决方案

原代码用sns.barplot的hue参数时,会为所有hue类别预留位置,导致无数据的机器出现空柱子,而且后续刻度标签是按固定重复实验名生成的,所以会有多余标签。我们可以手动为每个机器的现有实验绘制柱子,动态调整位置:

修改后的代码:

from matplotlib import pyplot as plt
from matplotlib import transforms
import numpy as np
import pandas as pd
import seaborn as sns

df_2 = pd.DataFrame({'Machine Number':  ['Machine 1', 'Machine 1', 'Machine 2','Machine 3','Machine 4','Machine 4'],
             'Experiment': ['Run 1','Run 2','Run 1', 'Run 1','Run 1','Run 2'],
             'Result': [10,32,71, 11,13,7]
             })

# 按机器分组,获取每个机器的实验和结果
machine_groups = df_2.groupby('Machine Number')
machines = list(machine_groups.groups.keys())
all_experiments = df_2['Experiment'].unique()
palette = sns.color_palette('Reds', len(all_experiments))
exp_color_map = dict(zip(all_experiments, palette))

fig, ax = plt.subplots(figsize=(12, 4))

# 配置柱子宽度和机器间距
machine_spacing = 0.8  # 机器之间的间距
bar_width_base = 0.3   # 单根柱子的基础宽度
current_x = 0
experiment_positions = []
machine_centers = []

for machine in machines:
    group_data = machine_groups.get_group(machine)
    num_exps = len(group_data)
    # 计算当前机器内柱子的总宽度和每个柱子的位置
    total_width = num_exps * bar_width_base
    # 居中放置当前机器的柱子组
    start_x = current_x + (machine_spacing - total_width) / 2
    
    for idx, (exp, result) in enumerate(zip(group_data['Experiment'], group_data['Result'])):
        bar_x = start_x + idx * bar_width_base
        ax.bar(bar_x, result, width=bar_width_base, color=exp_color_map[exp])
        # 记录柱子中心位置,用于刻度标签
        experiment_positions.append(bar_x + bar_width_base/2)
    
    # 记录机器组的中心位置
    machine_centers.append(start_x + total_width/2)
    # 更新下一个机器的起始位置
    current_x += machine_spacing

# 设置x轴刻度和标签:只显示有数据的实验
ax.set_xticks(experiment_positions)
ax.set_xticklabels([df_2.loc[df_2['Machine Number']==m, 'Experiment'].values[i] 
                    for m in machines for i in range(len(machine_groups.get_group(m)))], 
                   rotation=30)
ax.set_xlabel('')

# 添加机器分组标签和分隔线
trans = transforms.blended_transform_factory(ax.transData, ax.transAxes)
for pos, machine in zip(machine_centers, machines):
    ax.text(pos, -0.25, machine, transform=trans, ha='center', va='bottom', color='steelblue', fontsize=14)

# 添加机器之间的分隔线
for i in range(len(machine_centers)-1):
    # 计算两个机器中心之间的中点作为分隔线位置
    split_pos = (machine_centers[i] + machine_centers[i+1])/2
    ax.axvline(split_pos, 0, -0.25, color='steelblue', ls=':', clip_on=False)

# 添加图例
handles = [plt.Rectangle((0,0),1,1, color=exp_color_map[exp]) for exp in all_experiments]
ax.legend(handles, all_experiments, title='Experiment')

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
plt.tight_layout()
plt.show()

# st.pyplot(fig)

关键改动说明

  • 放弃sns.barplot的自动分组,改为手动遍历每个机器的现有实验绘制柱子,避免为空实验预留位置
  • 动态计算每个机器内柱子的位置,确保每组柱子居中,机器之间保持固定间距
  • x轴刻度只对应有数据的实验,不再生成多余标签
  • 重新计算机器分组的中心位置,确保标签和分隔线位置准确
  • 手动创建图例,保留所有实验类型的标识

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 19:18:17