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

如何在Matplotlib分组柱状图中添加双数据标签(含变化百分比标签)

Adding Percentage Change Labels to Your Bar Charts

Great question! To add those percentage change labels (like the red 33% in your example), you'll need to:

  1. Calculate the percentage change values for each bar using the original baseline values
  2. Manually place text annotations above or below each bar based on whether the change is positive or negative

Here's the modified code that implements this, with comments explaining the key parts:

import matplotlib.pyplot as plt
import numpy as np

value_case0_system1 = np.array([30, 20, 40])
value_case1_system1 = np.array([20, 25, 50])
value_case2_system1 = np.array([10, 35, 45])
value_case1_system2 = np.array([60, 50, 40])
value_case2_system2 = np.array([50, 40, 55])

change_case0_to_case1_system1 = np.subtract(value_case1_system1,value_case0_system1)
change_case1_to_case2_system1 = np.subtract(value_case2_system1,value_case1_system1)
change_case1_to_case2_system2 = np.subtract(value_case2_system2,value_case1_system2)

# Calculate percentage changes using the formula: ((new value - old value)/old value)*100
percent_case0_to_case1_system1 = (change_case0_to_case1_system1 / value_case0_system1) * 100
percent_case1_to_case2_system1 = (change_case1_to_case2_system1 / value_case1_system1) * 100
percent_case1_to_case2_system2 = (change_case1_to_case2_system2 / value_case1_system2) * 100

fig, (ax0, ax1) = plt.subplots(nrows=1, ncols=2, sharey=True, figsize=(18,10))
labels = ['Group 1', 'Group 2', 'Group 3']
x = np.arange(len(labels))

ax0.set_ylabel('Change in Values', va='center', rotation='vertical', fontsize=17, fontweight='bold')
width = 0.28

ax0.set_title('System 1', fontsize=17, fontweight='bold')
ax0.axhline(y=0, color='black', ls=':', lw=2)
ax0.set_xticks(x)
ax0.set_xticklabels(labels,fontsize=15)
rects1 = ax0.bar(x-width/2, change_case0_to_case1_system1, width, label='Case 0 to Case 1', color='#292929', edgecolor='black', linewidth=1)
rects2 = ax0.bar(x+width/2, change_case1_to_case2_system1, width, label='Case 1 to Case 2', color='#7f6d5f', edgecolor='black', linewidth=1)
ax0.bar_label(rects1, padding=3, fontsize=11)
ax0.bar_label(rects2, padding=3, fontsize=11)
leg = ax0.legend(loc="upper left", bbox_to_anchor=[0, 1], ncol=1, fancybox=True)
ax0.legend(fontsize=15)

# Add percentage labels for System 1 (Case 0 to Case 1)
for rect, pct in zip(rects1, percent_case0_to_case1_system1):
    height = rect.get_height()
    # Adjust position: above value label if positive, below if negative
    y_pos = height + 3 + 1.5 if height > 0 else height - 3 - 1.5
    # Set vertical alignment to keep text properly positioned
    va = 'bottom' if height > 0 else 'top'
    ax0.text(rect.get_x() + rect.get_width()/2., 
             y_pos,
             f'{pct:.0f}%',  # Format to integer percentage (use :.1f for 1 decimal place)
             ha='center', va=va,
             color='red', fontsize=11)

# Add percentage labels for System 1 (Case 1 to Case 2)
for rect, pct in zip(rects2, percent_case1_to_case2_system1):
    height = rect.get_height()
    y_pos = height + 3 + 1.5 if height > 0 else height - 3 - 1.5
    va = 'bottom' if height > 0 else 'top'
    ax0.text(rect.get_x() + rect.get_width()/2., 
             y_pos,
             f'{pct:.0f}%',
             ha='center', va=va,
             color='red', fontsize=11)

ax1.set_title('System 2', fontsize=17, fontweight='bold')
ax1.axhline(y=0, color='black', ls=':', lw=2)
ax1.set_xticks(x)
ax1.set_xticklabels(labels,fontsize=15)
rects3 = ax1.bar(x, change_case1_to_case2_system2, width, label='Case 1 to Case 2', color='#7f6d5f', edgecolor='black', linewidth=1)
ax1.legend(shadow=True, fancybox=True)
ax1.bar_label(rects3, padding=3, fontsize=11)
leg = ax1.legend(loc="upper left", bbox_to_anchor=[0, 1], ncol=1, fancybox=True)
ax1.legend(fontsize=15)

# Add percentage labels for System 2 (Case 1 to Case 2)
for rect, pct in zip(rects3, percent_case1_to_case2_system2):
    height = rect.get_height()
    y_pos = height + 3 + 1.5 if height > 0 else height - 3 - 1.5
    va = 'bottom' if height > 0 else 'top'
    ax1.text(rect.get_x() + rect.get_width()/2., 
             y_pos,
             f'{pct:.0f}%',
             ha='center', va=va,
             color='red', fontsize=11)

plt.tight_layout()
plt.show()

Key Details:

  • Accurate Percentage Calculation: We use the original baseline values (not just the difference) to compute meaningful relative percentage changes.
  • Dynamic Label Placement: Labels are positioned outside the existing value labels—above for positive changes, below for negative ones—so they don't overlap.
  • Customizable Formatting: The percentage is formatted as an integer (adjust :.0f to :.1f if you want decimal places) and colored red to match your example.

This implementation will give you the exact effect you're looking for, with properly positioned percentage labels for every bar in your chart.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.30 13:37:37