Python matplotlib dodged bar (series, data and category)

Question:

I have series, data and categories that I feed into a function to create a dodged bar using matplotlib.

I have managed to created a stacked chart, however I want to create a dodged bar.

This is what I have managed to create (stacked bar):
enter image description here

This is what I want to create (dodged bar):
enter image description here

#
# File: bar_dodged.py
# Version 1
# License: https://opensource.org/licenses/GPL-3.0 GNU Public License
#


import matplotlib.pyplot as plt
import numpy as np


def bar_dodged(series_labels: list = ['Minor', 'Low'],
        data: list = [
                [1, 2, 3, 4],
                [5, 6, 7, 8]
            ],
        category_labels: list = ['01/2023', '02/2023', '03/2023', '04/2023'],
        bar_background_colors: list = ['tab:orange', 'tab:green'],
        bar_text_colors: list = ['white', 'grey'],
        direction: str = "vertical",
        x_labels_rotation: int = 0,
        y_label: str = "Quantity (units)",
        figsize: tuple = (18, 5),
        reverse: bool = False,
        file_path: str = ".",
        file_name: str = "bar_dodged.png"):
    """

    :param series_labels:
    :param data:
    :param category_labels:
    :param bar_background_colors:
    :param bar_text_colors:
    :param direction:
    :param x_labels_rotation:
    :param y_label:
    :param figsize:
    :param reverse:
    :param file_path:
    :param file_name:
    :return:
    """
    # Debugging
    print(f"n")
    print(f"bar_dodged() :: series_labels={series_labels}")
    print(f"bar_dodged() :: data={data}")
    print(f"bar_dodged() :: category_labels={category_labels}")
    print(f"bar_dodged() :: bar_background_colors={bar_background_colors}")

    # Set size
    plt.figure(figsize=figsize)


    # Plot!
    show_values = True
    value_format = "{:.0f}"
    grid = False
    ny = len(data[0])
    ind = list(range(ny))

    axes = []
    cum_size = np.zeros(ny)

    data = np.array(data)

    if reverse:
        data = np.flip(data, axis=1)
        category_labels = reversed(category_labels)

    for i, row_data in enumerate(data):
        color = bar_background_colors[i] if bar_background_colors is not None else None
        axes.append(plt.bar(ind, row_data, bottom=cum_size,
                            label=series_labels[i], color=color))
        cum_size += row_data

    if category_labels:
        plt.xticks(ind, category_labels)

    if y_label:
        plt.ylabel(y_label)

    plt.legend()

    if grid:
        plt.grid()

    if show_values:
        for axis in axes:
            for bar in axis:
                w, h = bar.get_width(), bar.get_height()
                plt.text(bar.get_x() + w/2, bar.get_y() + h/2,
                         value_format.format(h), ha="center",
                         va="center")

    # Rotate
    plt.xticks(rotation=x_labels_rotation)

    # Two  lines to make our compiler able to draw:
    plt.savefig(f"{file_path}/{file_name}", bbox_inches='tight', dpi=200)


if __name__ == '__main__':
    # Usage example:

    series_labels = ['Globally', 'Customer']
    data = [[9, 6, 5, 4, 8], [8, 5, 4, 3, 7]]
    category_labels = ['Feb/2023', 'Dec/2022', 'Nov/2022', 'Oct/2022', 'Sep/2022']
    bar_background_colors = ['#800080', '#ffa503']

    bar_dodged(series_labels=series_labels, data=data, category_labels=category_labels,
               bar_background_colors=bar_background_colors)

What do I have to change in my code in order to make the chart dodged?

Asked By: Europa

||

Answers:

To do that, you need to change just one line inside the for loop where you are drawing the bars. Change the axes.append() to below…

axes.append(plt.bar([element + 0.2*i for element in ind], 
                    row_data, width = 0.2, #bottom=cum_size,
                    label=series_labels[i], color=color))

This will basically change the x position of the bar to 0,1,2.. (for the first run when 1=0) and add 0.2 for the second run. That that I have kept the bar width as 0.2 using width = 0.2. YOu can change it if you want thicker bars. Also, I have removed the bottom, which means, each bar/rectangle will be starting at 0. Hope this is what you are looking for…

Plot

enter image description here

Answered By: Redox
Categories: questions Tags: ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.