I'm using Python 3.9.7
I have some data that I'd like to plot as bar charts (sample code and pic below).
I want to plot the bar charts using a logarithmic y scale.
However, some of the bar values might be zero, so they won't show as a bar on the log scale, but I would still like to show the label for each bar even if it is zero. If I plot the y-axis linearly, the 0 label shows up, but not with log-scaling.
Is there a way to do this?
I'm not wedded to matplotlib if there are other ways of plotting.
Thank you.
import matplotlib.pyplot as plt
import numpy as np
month1 = [11, 1200, 0]
month2 = [55, 14, 37]
month3 = [111, 222, 300]
labels = ['a','b','c']
x_positions = np.arange(len(labels))
bar_width = 0.15
fig, ax = plt.subplots()
rects1 = ax.bar(x_positions - bar_width, month1, bar_width, label=labels[0])
rects2 = ax.bar(x_positions, month2, bar_width, label=labels[1])
rects3 = ax.bar(x_positions bar_width, month3, bar_width, label=labels[2])
# Add some text for labels, title and custom x-axis tick labels, etc.
ax.set_ylabel('Count')
ax.set_xticks(x_positions, labels)
ax.set_yscale('log') # the 0 label will appear if I omit this line.
ax.legend()
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
ax.bar_label(rects3, padding=3)
fig.tight_layout()
plt.show()
CodePudding user response:
The zero bar label is not shown, because on this log scale, 0 is infinitely far below the other bars' tops, so technically it can't be seen.
You can of course add a label manually:
ax.text(x = x_positions[2] - bar_width,
y = ax.get_ylim()[0] 1,
s = '0',
horizontalalignment='center')
The 1
is there to match the padding=3
of the other labels. You may need to change this for other scales.
This approach could be automated by iterating over all the values, e.g. like this (setting two more y values to zero for testing):
month1 = [11, 1200, 0]
month2 = [55, 0, 37]
month3 = [0, 222, 300]
labels = ['a', 'b', 'c']
x_positions = np.arange(len(labels))
bar_width = 0.15
y_min = 10
fig, ax = plt.subplots()
fig.tight_layout()
ax.set_yscale('log')
ax.set_ylim(y_min, 2000)
rects1 = ax.bar(x_positions - bar_width, month1, bar_width, label=labels[0])
rects2 = ax.bar(x_positions, month2, bar_width, label=labels[1])
rects3 = ax.bar(x_positions bar_width, month3, bar_width, label=labels[2])
ax.set_ylabel('Count')
ax.set_xticks(x_positions, labels)
ax.legend()
ax.bar_label(rects1, padding=3)
ax.bar_label(rects2, padding=3)
ax.bar_label(rects3, padding=3)
for x, month in enumerate([month1, month2, month3]):
for x_offset, y in zip([-1, 0, 1], month):
if y < y_min:
ax.text(x = x x_offset * bar_width,
y = y_min 1,
s = str(y),
horizontalalignment='center')