John Stud
John Stud

Reputation: 1779

Matplotlib: place text inside barplot?

I am wondering if it is possible for matplotlib to place text annotations inside each bar plot, even if the bar is very small?

A MWE is as follows:


import matplotlib.pyplot as plt
import numpy as np
vals = [0.09, 0.08, 0.05, 0.03, 0.03, 0.02, 0.01]
titles = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6', 'cat7']
color_list = ['#FF9349'] * len(vals)

# general vals
base = 2.43
width = 0.05
fig_w = 4
fig_h = 4
lower = []
upper = []
# calculate lower and upper ranges
for i in range(len(vals)):
    lower.append(base)
    upper.append(base - vals[i])
    base -= vals[i]
x = np.arange(0, width*len(upper), width)
height = [upper[i] - lower[i] for i in range(len(upper))]
x_axis_labs = [' {}         {}'.format(perc, val) for perc, val in zip(vals, titles)]
# init figure
plt.figure(figsize=(fig_w, fig_h))
plt.bar(x=x,
        height=height,
        bottom=lower,
        color=color_list,
        width=width)
# no borders
plt.box(False)
plt.tick_params(left=False, right=False, labelleft=False, labelbottom=False,
                bottom=False)
# annotations
for i in range(len(x_axis_labs)):
    plt.text(
             x=x[i],
             y=lower[i]-0.01,  # subtract a little more
             s=x_axis_labs[i],
             size=10,
             rotation=270,
             verticalalignment='center_baseline',
             )
plt.tight_layout()
plt.margins(x=0)
plt.margins(y=0)
plt.show()

Yielding:

enter image description here

While some cosmetic tweaks could be done, I am wondering if it is feasible to get these tiny values inside the bar plot without having to use a font size that is just too small to read.

A more cosmetic tweak but the text will still be too big for the bar:

enter image description here

Is there another way to go about annotations that would scale a bar plot's size to accommodate text inside very small values (e.g., plotting 0.01)

Upvotes: 0

Views: 309

Answers (2)

I'mahdi
I'mahdi

Reputation: 24049

I made a small change in your code. Does this help you?

x_axis_labs = [' {} {}'.format(perc, val) for perc, val in zip(vals, titles)] -> x_axis_labs = ['{} \n {}'.format(perc, val) for perc, val in zip(vals, titles)]

and

rotation=270
->
rotation=0

finally your code :

import matplotlib.pyplot as plt
import numpy as np
vals = [0.09, 0.08, 0.05, 0.03, 0.03, 0.02, 0.01]
titles = ['cat1', 'cat2', 'cat3', 'cat4', 'cat5', 'cat6', 'cat7']
color_list = ['#FF9349'] * len(vals)

# general vals
base = 2.43
width = 0.05
fig_w = 4
fig_h = 4
lower = []
upper = []
# calculate lower and upper ranges
for i in range(len(vals)):
    lower.append(base)
    upper.append(base - vals[i])
    base -= vals[i]
x = np.arange(0, width*len(upper), width)
height = [upper[i] - lower[i] for i in range(len(upper))]
x_axis_labs = ['{} \n {}'.format(perc, val) for perc, val in zip(vals, titles)]
# init figure
plt.figure(figsize=(fig_w, fig_h))
plt.bar(x=x,
        height=height,
        bottom=lower,
        color=color_list,
        width=width)
# no borders
plt.box(False)
plt.tick_params(left=False, right=False, labelleft=False, labelbottom=False,
                bottom=False)
# annotations
for i in range(len(x_axis_labs)):
    plt.text(
             x=x[i]-0.015,
             y=lower[i]-0.015,  # subtract a little more
             s=x_axis_labs[i],
             size=11,
             rotation=0,
             verticalalignment='center_baseline',
             )
plt.tight_layout()
plt.margins(x=0)
plt.margins(y=0)
plt.show()

output:

enter image description here

Upvotes: 1

Bastian
Bastian

Reputation: 518

One idea is to use a logarithmic scale on an axis. Another idea is to shift the annotations out of the small bar boxes. This can be done by changing the y value depending on the values:

# annotations
for i in range(len(x_axis_labs)):
    plt.text(
             x=x[i],
             y=lower[i] - (0.01 if vals[i] >= 0.05 else vals[i] + 0.01),  # subtract a little more
             s=x_axis_labs[i],
             size=10,
             rotation=270,
             verticalalignment='center_baseline',
             )

annotations

Upvotes: 1

Related Questions