如何扩展Matplotlib分组柱状图以展示全季度Budget与Actual数据
修改代码实现全季度分组柱状图
核心修改思路
- 用循环遍历Q1-Q4四个季度,避免重复编写柱状图绘制代码
- 计算每个季度柱子的偏移量,确保同一部门下的季度柱子有序排列、不重叠
- 动态适配部门数量,替换硬编码的数值
- 优化图例与布局,提升图表可读性
修改后的完整代码(两种实现方式)
方式一:基于原数据结构直接循环
import pandas as pd import numpy as np import matplotlib.pyplot as plt df = pd.read_excel(file) cols = df['Dept.'] df = df.T df.columns = cols df = df.tail(-1) # 移除转置后的原表头行 # 基础配置 n_depts = len(cols) quarters = ['Q1', 'Q2', 'Q3', 'Q4'] single_bar_width = 0.1 colors = {'Actual': 'cornflowerblue', 'Budget': 'cornflowerblue'} alphas = {'Actual': 1, 'Budget': 0.2} x = np.arange(n_depts) # 循环绘制每个季度的Actual和Budget柱子 for idx, quarter in enumerate(quarters): # 计算当前季度的整体偏移量,让柱子均匀分布在部门刻度两侧 quarter_offset = (idx - 1.5) * 2 * single_bar_width # 绘制Actual数据 plt.bar( x + quarter_offset, df.loc[f'{quarter} Actual'], width=single_bar_width, color=colors['Actual'], alpha=alphas['Actual'], label=f'{quarter} Actual' if idx == 0 else "" ) # 绘制Budget数据 plt.bar( x + quarter_offset + single_bar_width, df.loc[f'{quarter} Budget'], width=single_bar_width, color=colors['Budget'], alpha=alphas['Budget'], label=f'{quarter} Budget' if idx == 0 else "" ) # 图表样式配置 plt.xticks(x, cols, rotation=45) plt.legend(title='Quarter & Type') plt.title('Q1-Q4 Budget vs Actual by Department') plt.xlabel('Department') plt.ylabel('Amount') plt.tight_layout() # 自动调整布局,避免标签截断 plt.show()
方式二:数据重塑为长格式(更灵活)
如果后续需要扩展更多季度/指标,推荐这种方式:
import pandas as pd import numpy as np import matplotlib.pyplot as plt df = pd.read_excel(file) # 将宽表转为长格式,方便分组处理 df_melt = df.melt(id_vars='Dept.', var_name='Category', value_name='Amount') # 拆分季度和指标类型(Budget/Actual) df_melt[['Quarter', 'Type']] = df_melt['Category'].str.split(' ', expand=True) # 基础配置 n_depts = len(df['Dept.']) quarters = df_melt['Quarter'].unique() types = df_melt['Type'].unique() single_bar_width = 0.1 x = np.arange(n_depts) # 循环绘制每个季度、每个类型的柱子 for q_idx, quarter in enumerate(quarters): for t_idx, typ in enumerate(types): # 计算当前柱子的偏移量 bar_offset = (q_idx * len(types) + t_idx - (len(quarters)*len(types)-1)/2) * single_bar_width # 筛选对应数据 filtered_data = df_melt[(df_melt['Quarter'] == quarter) & (df_melt['Type'] == typ)] # 绘制柱子 plt.bar( x + bar_offset, filtered_data['Amount'], width=single_bar_width, label=f'{quarter} {typ}' if q_idx == 0 else "", color='cornflowerblue', alpha=1 if typ == 'Actual' else 0.2 ) # 图表样式配置 plt.xticks(x, df['Dept.'], rotation=45) plt.legend(title='Quarter & Type') plt.title('Q1-Q4 Budget vs Actual by Department') plt.xlabel('Department') plt.ylabel('Amount') plt.tight_layout() plt.show()
关键修改点说明
- 循环遍历季度:避免重复编写四次
plt.bar,代码更简洁易维护 - 动态偏移计算:确保同一部门下的8个柱子(4季度×2类型)均匀分布,不会重叠
- 动态部门数量:用
len(cols)代替硬编码的4,适配任意数量的部门 - 图例优化:仅在第一次循环时添加图例标签,避免重复显示
- 布局调整:
plt.tight_layout()自动调整图表元素位置,防止x轴部门标签被截断
内容的提问来源于stack exchange,提问作者pyth0nEiken
相关产品推荐
相关产品推荐

