如何在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
相关产品推荐
相关产品推荐

