How to create a categorized progress bar

Viewed 74

I am trying to create a status bar that is similar to something we see currently in a different reporting tool (see attached image). I want to use matplotlib to generate this as an image, that way it could be used in several different places and reports.

I cannot find a good guide that can walk me through how to do this using Matplotlib in particular. I am passing in the data as:

results = {'pass': 5, 'fail':18, 'na': 10, 'todo': 187, 'blocked': 12, 'aborted': 10}

I would like it to look similar to this chart:

Bar Chart

Can anyone guide me to the right resources?

1 Answers

I took the code of the documentation and apply some changes in order to apply it to your case.

import matplotlib.pyplot as plt
import numpy as np

results = {'Pass': 5, 'Fail':18, 'Na': 10, 'Todo': 187, 'Blocked': 12, 'Aborted': 10}

category_names = [f'{value} {category.upper()}' for category, value in results.items()]

results = {'1': list(results.values())}

labels = list(results.keys())
data = np.array(list(results.values()))
data_cum = data.cumsum(axis = 1)
category_colors = plt.get_cmap('RdYlGn')(np.linspace(0.15, 0.85, data.shape[1]))

fig = plt.figure(figsize = (10, 2))
ax = fig.add_axes([0.1, 0.4, 0.8, 0.4])

ax.axis('off')

for i, (colname, color) in enumerate(zip(category_names, category_colors)):
    widths = data[:, i]
    starts = data_cum[:, i] - widths
    ax.barh(labels, widths, left = starts, height = 0.5, label = colname, color = color)

ax.legend(ncol = len(category_names), bbox_to_anchor = (0, 0), loc = 'upper left', fontsize = 10, frameon = False)

plt.show()

enter image description here

If you want to format the legend too, you can replace ax.legend line above with (you have to import transforms with from matplotlib import transforms):

handles, labels = ax.get_legend_handles_labels()

for label, color, position in zip(labels, category_colors, np.linspace(0, 220, len(labels))):
    text = plt.text(position, -0.5, label.split()[0] + ' ', color = color, size = 15, horizontalalignment = 'right')
    text.draw(fig.canvas.get_renderer())
    ex = text.get_window_extent()
    transforms.offset_copy(text._transform, y = ex.height, units = 'dots')
    text = plt.text(position + 2, -0.5, label.split()[1], color = 'black', size = 10, horizontalalignment = 'left')
    text.draw(fig.canvas.get_renderer())
    ex = text.get_window_extent()
    transforms.offset_copy(text._transform, y = ex.height, units = 'dots')

enter image description here

In this case you have to optimize:

  • figure size (figsize = (10, 2))
  • total length of the legend (np.linspace(0, 220, len(labels)))
  • space between text (position + 2)
  • text size (size = 15)

based on your needs.

Related