如何基于共享时间戳的三个分组DataFrame绘制堆叠柱状图?
问题
我有三个分组DataFrame,都包含TIMESTAMP、类别列和count列,且共享相同时间戳。单个DataFrame可以用以下代码成功绘制堆叠柱状图:
df.pivot_table(index='timestamp', columns='category1', values='count', aggfunc='mean').plot(kind='bar', stacked=True)
但我想在同一画布的三个子轴上分别绘制这三个DataFrame的堆叠柱状图,尝试下面的代码却无法运行:
fig, (ax1, ax2, ax3) = plt.subplots(1, 3) ax1.bar(x=df.index, y='category1', data=df1, stacked=True) ax2.bar(x=df.index, y='category2', data=df2, stacked=True) ax3.bar(x=df.index, y='category3', data=df3, stacked=True)
不能使用Seaborn库,看到过循环解决方案但没明确对应列,求可行建议。
数据示例:
TIMESTAMP CATEGORY_1 count 0 2023-03-31 correct 30 1 2023-03-31 not correct 11 2 2023-03-31 no info 2 3 2023-04-30 correct 15 4 2023-04-30 not correct 8 TIMESTAMP CATEGORY_2 count 0 2023-03-31 okay 29 1 2023-03-31 no info 17 2 2023-03-31 too high 4 TIMESTAMP CATEGORY_3 count 0 2023-03-31 okay 4 1 2023-03-31 no info 2 2 2023-03-31 positive 3 3 2023-03-31 negative 2
解决方案
你之前的代码跑不通,是因为原生matplotlib的ax.bar方法里的stacked参数根本不是用来实现堆叠的(这个参数是控制水平柱状图的对齐方式,和堆叠逻辑无关)。其实不用折腾原生bar,直接沿用你熟悉的pandas透视+绘图逻辑,指定每个图的子轴就行,简单高效。
方法1:逐个处理每个DataFrame
直接对每个DataFrame做透视,然后把图画到对应的子轴上:
import matplotlib.pyplot as plt # 创建1行3列的画布和子轴,设置合适的尺寸 fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 6)) # 第一个子轴:处理df1并绘图 df1_pivot = df1.pivot_table(index='TIMESTAMP', columns='CATEGORY_1', values='count', aggfunc='mean') df1_pivot.plot(kind='bar', stacked=True, ax=ax1) ax1.set_title('CATEGORY_1 堆叠柱状图') ax1.tick_params(axis='x', rotation=45) # 旋转x轴标签避免重叠 # 第二个子轴:处理df2并绘图 df2_pivot = df2.pivot_table(index='TIMESTAMP', columns='CATEGORY_2', values='count', aggfunc='mean') df2_pivot.plot(kind='bar', stacked=True, ax=ax2) ax2.set_title('CATEGORY_2 堆叠柱状图') ax2.tick_params(axis='x', rotation=45) # 第三个子轴:处理df3并绘图 df3_pivot = df3.pivot_table(index='TIMESTAMP', columns='CATEGORY_3', values='count', aggfunc='mean') df3_pivot.plot(kind='bar', stacked=True, ax=ax3) ax3.set_title('CATEGORY_3 堆叠柱状图') ax3.tick_params(axis='x', rotation=45) # 自动调整子轴间距,避免标签重叠 plt.tight_layout() plt.show()
方法2:用循环简化代码(减少重复)
如果三个DataFrame的处理逻辑完全一致,可以把相关参数打包成列表,用循环批量处理,避免重复写代码:
import matplotlib.pyplot as plt # 打包需要处理的参数:(DataFrame对象, 类别列名称, 目标子轴, 图表标题) plot_configs = [ (df1, 'CATEGORY_1', ax1, 'CATEGORY_1 堆叠柱状图'), (df2, 'CATEGORY_2', ax2, 'CATEGORY_2 堆叠柱状图'), (df3, 'CATEGORY_3', ax3, 'CATEGORY_3 堆叠柱状图') ] # 创建画布和子轴 fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(15, 6)) # 循环处理每个配置 for df, cat_col, ax, title in plot_configs: # 透视表转换 pivot_df = df.pivot_table(index='TIMESTAMP', columns=cat_col, values='count', aggfunc='mean') # 绘制堆叠柱状图到指定子轴 pivot_df.plot(kind='bar', stacked=True, ax=ax) # 设置标题和x轴标签旋转 ax.set_title(title) ax.tick_params(axis='x', rotation=45) plt.tight_layout() plt.show()
补充说明
原生ax.bar要实现堆叠,需要手动计算每个类别的底部位置(比如第一个类别底部是0,第二个类别底部是第一个类别的数值总和,以此类推),逻辑繁琐容易出错。而pandas的plot接口已经封装了堆叠的计算逻辑,只需要通过ax参数指定子轴,就能轻松把图画到对应位置,完全满足你的需求。
内容的提问来源于stack exchange,提问作者gulniza
相关产品推荐
相关产品推荐

