Home > Software engineering >  matplotlib bar chart with log axis showing labels for bars with zero values
matplotlib bar chart with log axis showing labels for bars with zero values

Time:05-04

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.

 and

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')

Barplot with log axis and three zero labels

  • Related