grokkingstuff Home Blog Projects Wiki Calculators About

Create barplot in matplotlib

Classic barplot

Library imports

Library imports

import numpy as np              # Manipulating numbers and arrays
import pandas as pd             # Manipulating tabular data
import matplotlib as mpl        # For setting parameters, we need to call mpl directly
import matplotlib.pyplot as plt # The pythonic way of invoking matplotlib
print("Hello")

#+RESULTS: : Hello

Setup variables

legend_titleThe title for the legend in a graph.
titleThe title of the graph.
labelsA list of names on the x axis.
valuesA list of values on the y axis.
bar_labelsA list of labels for the x axis.
bar_colorsA list of colors assigned to each bar.
ylabelThe label for the y-axis of the graph.
fig_pathThe file name for the output image.

Setup variables

# Inputs
title = 'Fruit supply by kind and color'
legend_title = 'Fruit color'
ylabel = 'fruit supply'

labels = ['apple', 'blueberry', 'cherry', 'orange']
values = [40, 120, 30, 55]
bar_colors = ['tab:green', 'tab:blue', 'tab:red', 'tab:orange']

# Outputs
fig_path = "fig.png"

#+RESULTS:

Setup variables

fig, ax = plt.subplots()

for label, value, bar_color in zip(labels, values, bar_colors):
    # --- this is the line we changed --- #
    ax.bar(label, value, color=bar_color)
 #   ax.annotate(r"%d" % value,
 #               (label, value + 100), va="bottom", ha="center")


ax.set_ylabel(ylabel)
ax.set_title(title)
ax.legend(title=legend_title)

legend_elements = [plt.Rectangle((0, 0), 1, 1, color=color, label=label)
                   for color, label in zip(bar_colors, labels)]
ax.legend(handles=legend_elements, title=legend_title)

ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')

plt.show()
fig.savefig(fig_path, dpi=600)

#+RESULTS: :RESULTS: : No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. [image: 28a6f413ed999a6554ba03d1cdb66ddce1ab40b5.png] :END:

Horizontal barplot

Library imports

import numpy as np              # Manipulating numbers and arrays
import pandas as pd             # Manipulating tabular data
import matplotlib as mpl        # For setting parameters, we need to call mpl directly
import matplotlib.pyplot as plt # The pythonic way of invoking matplotlib
print("Hello")
legend_titleThe title for the legend in a graph.
titleThe title of the graph.
labelsA list of names on the x axis.
valuesA list of values on the y axis.
bar_labelsA list of labels for the x axis.
bar_colorsA list of colors assigned to each bar.
ylabelThe label for the y-axis of the graph.
fig_pathThe file name for the output image.

Setup variables

# Inputs
title = 'Fruit supply by kind and color'
legend_title = 'Fruit color'
ylabel = 'fruit supply'

labels = ['apple', 'blueberry', 'cherry', 'orange']
values = [40, 120, 30, 55]
bar_colors = ['tab:green', 'tab:blue', 'tab:red', 'tab:orange']

# Outputs
fig_path = "fig.png"

#+RESULTS:

Setup variables

fig, ax = plt.subplots()

for label, value, bar_color in zip(labels, values, bar_colors):
    # --- this is the line we changed --- #
    ax.barh(label, value, color=bar_color)

ax.set_ylabel(ylabel)
ax.set_title(title)
ax.legend(title=legend_title)

legend_elements = [plt.Rectangle((0, 0), 1, 1, color=color, label=label)
                   for color, label in zip(bar_colors, labels)]
ax.legend(handles=legend_elements, title=legend_title)

ax.invert_yaxis()  # labels read top-to-bottom
ax.spines['top'].set_visible(False)
ax.spines['right'].set_visible(False)
ax.yaxis.set_ticks_position('left')
ax.xaxis.set_ticks_position('bottom')

plt.show()
fig.savefig(fig_path, dpi=600)

#+RESULTS: :RESULTS: : No artists with labels found to put in legend. Note that artists whose label start with an underscore are ignored when legend() is called with no argument. [image: f4752cd4aa607b6dd9c05ff2f3584be6e9cd1e71.png] :END:

Stacked bar charts

Library imports

import numpy as np              # Manipulating numbers and arrays
import pandas as pd             # Manipulating tabular data
import matplotlib as mpl        # For setting parameters, we need to call mpl directly
import matplotlib.pyplot as plt # The pythonic way of invoking matplotlib
print("Hello")
legend_titleThe title for the legend in a graph.
titleThe title of the graph.
labelsA list of names on the x axis.
valuesA list of values on the y axis.
bar_labelsA list of labels for the x axis.
bar_colorsA list of colors assigned to each bar.
ylabelThe label for the y-axis of the graph.
fig_pathThe file name for the output image.

Setup variables

# Inputs
title = 'Fruit supply by kind and color'
legend_title = 'Fruit color'
ylabel = 'fruit supply'

labels = ['apple', 'blueberry', 'cherry', 'orange']
values = [40, 120, 30, 55]
bar_colors = ['tab:green', 'tab:blue', 'tab:red', 'tab:orange']


species = (
    "Adelie\n $\\mu=$3700.66g",
    "Chinstrap\n $\\mu=$3733.09g",
    "Gentoo\n $\\mu=5076.02g$",
)
weight_counts = {
    "Below": np.array([70, 31, 58]),
    "Above": np.array([82, 37, 66]),
}
width = 0.5


# Outputs
fig_path = "fig.png"

Setup variables

fig, ax = plt.subplots()


bottom = np.zeros(3)

for boolean, weight_count in weight_counts.items():
    p = ax.bar(species, weight_count, width, label=boolean, bottom=bottom)
    bottom += weight_count


ax.set_title("Number of penguins with above average body mass")
ax.legend(loc="upper right")

plt.show()
fig.savefig(fig_path, dpi=600)

#+RESULTS: [image: da61814a611647bf1b1b88b528f891db456e3c29.png]

Discrete distribution as horizontal bar charts

import matplotlib.pyplot as plt
import numpy as np

category_names = ['Strongly disagree', 'Disagree',
                  'Neither agree nor disagree', 'Agree', 'Strongly agree']
results = {
    'Question 1': [10, 15, 17, 32, 26],
    'Question 2': [26, 22, 29, 10, 13],
    'Question 3': [35, 37, 7, 2, 19],
    'Question 4': [32, 11, 9, 15, 33],
    'Question 5': [21, 29, 5, 5, 40],
    'Question 6': [8, 19, 5, 30, 38]
}


def survey(results, category_names):
    """
    Parameters
    ----------
    results : dict
        A mapping from question labels to a list of answers per category.
        It is assumed all lists contain the same number of entries and that
        it matches the length of *category_names*.
    category_names : list of str
        The category labels.
    """
    labels = list(results.keys())
    data = np.array(list(results.values()))
    data_cum = data.cumsum(axis=1)
    category_colors = plt.colormaps['RdYlGn'](
        np.linspace(0.15, 0.85, data.shape[1]))

    fig, ax = plt.subplots(figsize=(9.2, 5))
    ax.invert_yaxis()
    ax.xaxis.set_visible(False)
    ax.set_xlim(0, np.sum(data, axis=1).max())

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

        r, g, b, _ = color
        text_color = 'white' if r * g * b < 0.5 else 'darkgrey'
        ax.bar_label(rects, label_type='center', color=text_color)
    ax.legend(ncols=len(category_names), bbox_to_anchor=(0, 1),
              loc='lower left', fontsize='small')

    return fig, ax


survey(results, category_names)
plt.show()

#+RESULTS: [image: 16d969fbcc423581d862f9ba4a829b4f8bfd7189.png]

Best practices for using bar charts

Figures should have a width of 174 mm for double column areas or 84mm for single column areas, and not higher than 234 mm in height.

First and foremost, make sure that all of your bars are being plotted against a zero-value baseline. Not only does that baseline make it easier for readers to compare bar lengths, it also maintains the truthfulness of your data visualization. A bar chart with a non-zero baseline or some other gap in the axis scale can easily misrepresent the comparison between groups since the ratio in bar lengths will not match the ratio in actual bar values.

Another major no-no is to mess with the shape of the bars to be plotted. Some tools will allow for the rounding of the bar caps, instead of straight edges. This rounding means that it’s difficult for the reader to tell where to read the actual value: from the top of the semicircle, or somewhere in the middle? A little bit of rounding of the corners can be okay, but make sure each bar is flat enough to discern its true value and provide an easy comparison between bars.

Similarly, you should avoid including 3-d effects on your bars. As with heavy rounding, this can make it harder to know how to measure bar lengths, and as a bonus, might cause baselines to not be aligned (see the above point).

One consideration you should have when putting together a bar chart is what order in which you will plot the bars. A standard convention to take is to sort the bars from longest to shortest: while it is always possible to compare the bar lengths no matter the order, this can reduce the burden on the reader to make those comparisons themselves. The major exception to this is if the category labels are inherently ordered in some way. In cases like that, the inherent ordering usually takes precedence.

A common bar chart variation is whether or not the bar chart should be oriented vertically (with categories on the horizontal axis) or horizontally (with categories on the vertical axis). While the vertical bar chart is usually the default, it’s a good idea to use a horizontal bar chart when you are faced with long category labels. In a vertical chart, these labels might overlap, and would need to be rotated or shifted to remain legible; the horizontal orientation avoids this issue.

A common addition to bar charts are value annotations. While it is fairly easy for readers to compare bar lengths and gauge approximate values from a bar chart, exact values aren’t necessarily easy to state. Annotations can report these values where they are important, and are usually placed in the middle of the bar or at their ends.

When the numeric values are a summary measure, a frequent consideration is whether or not to include error bars in the plot. Error bars are additional whiskers added to the end of each bar to indicate variability in the individual data points that contributed to the summary measure. Since there are many choices for uncertainty measure (e.g. standard deviation, confidence interval, interquartile range) it is important that when you display error bars, that you note in an annotation or comment what the error bars represent.

Alternatively, you may wish to depict variance within each category with a different chart type such as the box plot or violin plot. While these plots will have more elements for a reader to parse, they provide a deeper understanding of the distribution of values within each group.