Add label values to bar chart and line chart
Question:
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?
Answers:
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')
- From
matplotlib v3.4.0
it’s easier to use matplotlib.pyplot.bar_label
, as explained in this answer.
- The OP has many extraneous steps, which can be removed by using the
yticks
, secondary_y
, and ylabel
parameters for pandas.DataFrame.plot
pandas.DataFrame.itertuples
can be used to annotate the line with matplotlib.axes.Axes.annotate
because .Index
corresponds to the x-axis locations and .percent
is the correct y value for ax2
.
- See How to add hovering annotations to a plot for additional options to annotate the line.
- See How to change the color of the axis, ticks and labels for a plot to easily change colors of various aspects of the figure.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# create the bar plot for the Count column and specify the yticks
ax = df.Count.plot(kind='bar', color='tab:orange', rot=0, yticks=range(0, 20001, 2500), figsize=(9, 5), ylabel='Counts')
# add bar labels
ax.bar_label(ax.containers[0])
# add the line plot for the percent column and specify the yticks and secondary_y
ax2 = df.percent.plot(marker='.', yticks=np.arange(0, 5, 0.5), secondary_y=True, ax=ax, ylabel='Percent')
# annotate the line by iterating through each row with itertuples
for row in df.itertuples():
ax2.annotate(text=row.percent, xy=(row.Index, row.percent))
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?
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')
- From
matplotlib v3.4.0
it’s easier to usematplotlib.pyplot.bar_label
, as explained in this answer. - The OP has many extraneous steps, which can be removed by using the
yticks
,secondary_y
, andylabel
parameters forpandas.DataFrame.plot
pandas.DataFrame.itertuples
can be used to annotate the line withmatplotlib.axes.Axes.annotate
because.Index
corresponds to the x-axis locations and.percent
is the correct y value forax2
.- See How to add hovering annotations to a plot for additional options to annotate the line.
- See How to change the color of the axis, ticks and labels for a plot to easily change colors of various aspects of the figure.
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
# create the bar plot for the Count column and specify the yticks
ax = df.Count.plot(kind='bar', color='tab:orange', rot=0, yticks=range(0, 20001, 2500), figsize=(9, 5), ylabel='Counts')
# add bar labels
ax.bar_label(ax.containers[0])
# add the line plot for the percent column and specify the yticks and secondary_y
ax2 = df.percent.plot(marker='.', yticks=np.arange(0, 5, 0.5), secondary_y=True, ax=ax, ylabel='Percent')
# annotate the line by iterating through each row with itertuples
for row in df.itertuples():
ax2.annotate(text=row.percent, xy=(row.Index, row.percent))