Search code examples
pythonmatplotlibbar-chartlinechartplot-annotations

Add label values to bar chart and line chart


I have created a bar chart and a line chart using two different y-axes for the following dataframe.

import pandas as pd
import matplotlib.pyplot as plt

df = pd.DataFrame({'DXC':['T1', 'H1', 'HP', 'T1_or_H1_or_HP'], 
                   'Count': [2485, 5595, 3091, 9933],
                   'percent':[1.06, 2.39, 1.31, 4.23]}) 

              DXC  Count  percent
0              T1   2485     1.06
1              H1   5595     2.39
2              HP   3091     1.31
3  T1_or_H1_or_HP   9933     4.23

Using the following code, I can also display values next to each bar in the bar chart. However, I have not been successful thus far in my attempts to also display the label (percent) values for the line plot.

fig=plt.figure()
#AX: bar chart 
ax=df["Count"].plot(kind="bar", color="orange")
ax.set_ylabel("Counts")
ax.set_xlabel("")
ax.set_ylim(0,20000)

for tick in ax.get_xticklabels():
    tick.set_rotation(0)

#AX2: Create secondary y-axis with same x-axis as above for plotting percent values
ax2=ax.twinx()
ax2.plot(ax.get_xticks(),df["percent"], color="red", linewidth=4, marker = "o")
ax2.grid(False)
ax2.set_ylabel("Percent", color = "red")
ax2.set_ylim(0,4.5)
ax2.tick_params(labelcolor="red", axis='y')


def add_value_labels(ax, spacing=5):
    for i in ax.patches:
        y_value = i.get_height()
        x_value = i.get_x() + i.get_width() / 2
        space = spacing
        va = 'bottom'
        # Use Y value as label and format number with no decimal place
        label = "{:.0f}".format(y_value)
    # Create annotation
    ax.annotate(label,(x_value, y_value), xytext=(0, space), textcoords="offset points", ha='center',  va=va)                                                     

    
add_value_labels(ax)
plt.show()

Can somebody suggest how to display labels for both bar plot and line plot in the same figure?


Solution

  • Here is a modified function that will achieve the required task. The trick is to extract the x and y values based on the type of the chart you have. For a line chart, you can use ax.lines[0] and then get_xdata and get_ydata

    def add_value_labels(ax, typ, spacing=5):
        space = spacing
        va = 'bottom'
    
        if typ == 'bar':
            for i in ax.patches:
                y_value = i.get_height()
                x_value = i.get_x() + i.get_width() / 2
    
                label = "{:.0f}".format(y_value)
                ax.annotate(label,(x_value, y_value), xytext=(0, space), 
                        textcoords="offset points", ha='center', va=va)     
        if typ == 'line':
            line = ax.lines[0]
            for x_value, y_value in zip(line.get_xdata(), line.get_ydata()):
                label = "{:.2f}".format(y_value)
                ax.annotate(label,(x_value, y_value), xytext=(0, space), 
                    textcoords="offset points", ha='center', va=va)   
    
    add_value_labels(ax, typ='bar')
    add_value_labels(ax2, typ='line')
    

    enter image description here