如何在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:
- Calculate the percentage change values for each bar using the original baseline values
- 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
:.0fto:.1fif 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
相关产品推荐
相关产品推荐

