Automatic add text to matplotlib plot in Python

Viewed 235

I try to produce a plot and want to automatically add text (in this case is percentage) to each circle in correspond to each y axis types. Any help would be very helpful.

# import libraries
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# Make some data
index=['Stream flow',
 'Soil moisture',
 'Water indices',
 'Others',
 'Temperature',
 'Precipitation',
 'Vegetative indices']
value=[2.13, 6.38, 10.64, 12.77, 17.73, 21.99, 28.37]

# create dataframe
percentages = pd.Series(value,index=index)
df = pd.DataFrame({'percentage' : percentages})
df = df.sort_values(by='percentage')

# we first need a numeric placeholder for the y axis
my_range=list(range(1,len(df.index)+1))

fig, ax = plt.subplots(figsize=(15,8))

# create for each expense type an horizontal line that starts at x = 0 with the length 
plt.hlines(y=my_range, xmin=0, xmax=df['percentage']-0.5, color='black', alpha=0.8, linewidth=1)

# create for each expense type a dot at the level of the expense percentage value
line=plt.plot(df['percentage'], my_range, "o", markersize=30, color='#fd8c00', alpha=0.6, linewidth=0.3)

# set labels
ax.set_xlabel('Percentage', fontsize=15)
ax.set_ylabel('')

# set axis
ax.tick_params(axis='both', which='major', labelsize=14)
plt.yticks(my_range, df.index)


ax.set_xlim(0,30)
1 Answers

You can use matplotlib.axes.Axes.text:

x_space = 0.4
y_space = 0.05
fontsize = 7
for y_i, val in enumerate(value, 1):
    ax.text(x = val - x_space, y = y_i - y_space, s = f'{val}%', fontsize = fontsize)

You have to adjust x_space, y_space and fontsize in order to fit properly the text within the circles.

Complete code

# import libraries
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# Make some data
index=['Stream flow',
 'Soil moisture',
 'Water indices',
 'Others',
 'Temperature',
 'Precipitation',
 'Vegetative indices']
value=[2.13, 6.38, 10.64, 12.77, 17.73, 21.99, 28.37]

# create dataframe
percentages = pd.Series(value,index=index)
df = pd.DataFrame({'percentage' : percentages})
df = df.sort_values(by='percentage')

# we first need a numeric placeholder for the y axis
my_range=list(range(1,len(df.index)+1))

fig, ax = plt.subplots(figsize=(15,8))

# create for each expense type an horizontal line that starts at x = 0 with the length 
plt.hlines(y=my_range, xmin=0, xmax=df['percentage']-0.5, color='black', alpha=0.8, linewidth=1)

# create for each expense type a dot at the level of the expense percentage value
line=plt.plot(df['percentage'], my_range, "o", markersize=30, color='#fd8c00', alpha=0.6, linewidth=0.3)

# set labels
ax.set_xlabel('Percentage', fontsize=15)
ax.set_ylabel('')

# set axis
ax.tick_params(axis='both', which='major', labelsize=14)
plt.yticks(my_range, df.index)

ax.set_xlim(0,30)


x_space = 0.4
y_space = 0.05
for y_i, val in enumerate(value, 1):
    ax.text(x = val - x_space, y = y_i - y_space, s = f'{val:>5.2f}%', fontsize = 7)


plt.show()

enter image description here


Same code as above, but with increased circle radius and font, in order to improve readability.

# import libraries
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# Make some data
index=['Stream flow',
 'Soil moisture',
 'Water indices',
 'Others',
 'Temperature',
 'Precipitation',
 'Vegetative indices']
value=[2.13, 6.38, 10.64, 12.77, 17.73, 21.99, 28.37]

# create dataframe
percentages = pd.Series(value,index=index)
df = pd.DataFrame({'percentage' : percentages})
df = df.sort_values(by='percentage')

# we first need a numeric placeholder for the y axis
my_range=list(range(1,len(df.index)+1))

fig, ax = plt.subplots(figsize=(15,8))

# create for each expense type an horizontal line that starts at x = 0 with the length 
plt.hlines(y=my_range, xmin=0, xmax=df['percentage']-0.85, color='black', alpha=0.8, linewidth=1)

# create for each expense type a dot at the level of the expense percentage value
line=plt.plot(df['percentage'], my_range, "o", markersize=50, color='#fd8c00', alpha=0.6, linewidth=0.3)

# set labels
ax.set_xlabel('Percentage', fontsize=15)
ax.set_ylabel('')

# set axis
ax.tick_params(axis='both', which='major', labelsize=14)
plt.yticks(my_range, df.index)

ax.set_xlim(0,30)
ax.set_ylim(0, len(value) + 1)

x_space = 0.75
y_space = 0.06
fontsize = 12
for y_i, val in enumerate(value, 1):
    ax.text(x = val - x_space, y = y_i - y_space, s = f'{val:>5.2f}%', fontsize = fontsize)


plt.show()

enter image description here


Even better, you can use matplotlib.axes.Axes.annotate to get rid of x_space and y_space:

fontsize = 12
for y_i, x_i in enumerate(value, 1):
    ax.annotate(f'{x_i:>5.2f}%', xy = (x_i, y_i), xytext = (0, 0), textcoords = 'offset points', ha = 'center', va = 'center', fontsize = fontsize)

You still have to adjust the fontsize to properly fit the radius of the circles.

Related