Reputation: 1779
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:
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:
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
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:
Upvotes: 1
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',
)
Upvotes: 1