W3docs

Matplotlib Bar Charts in Python — Complete Guide

Learn to create vertical, horizontal, grouped, and stacked bar charts in Python with Matplotlib. Includes color, width, error bars, and saving examples.

Matplotlib's bar() and barh() functions let you build bar charts that compare quantities across categories. This chapter covers everything from the simplest single-series chart to grouped and stacked layouts, with practical customization options you will reach for in real projects: colors, bar width, edge styling, error bars, and saving the result to a file.

Before diving in, make sure Matplotlib is installed:

pip install matplotlib

If you are new to the library, see the Matplotlib Introduction and Getting Started chapters first.

What is a Bar Chart?

A bar chart uses rectangular bars whose length encodes a numeric value, making it easy to compare quantities across discrete categories — for example, monthly revenue, population by country, or test scores by subject.

Use a bar chart when:

  • You are comparing a single metric across categories (simple bar chart).
  • You want to show how a total breaks down into parts (stacked bar chart).
  • You need to compare multiple metrics side by side for the same categories (grouped bar chart).
  • Your category labels are long and read better horizontally (horizontal bar chart).

Creating a Basic Vertical Bar Chart

The plt.bar(x, height) function takes a sequence of category labels (or numeric positions) and a matching sequence of bar heights.

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]

plt.bar(categories, values)

plt.title('Sample Bar Chart')
plt.xlabel('Category')
plt.ylabel('Value')

plt.tight_layout()
plt.show()

plt.tight_layout() prevents axis labels from being clipped — a good habit to add before every show() or savefig() call.

Customizing Bar Appearance

Color

Pass a single color name, a hex string, or a list of per-bar colors to the color parameter:

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]

# One color for all bars
plt.bar(categories, values, color='steelblue')

plt.title('Steelblue Bars')
plt.xlabel('Category')
plt.ylabel('Value')
plt.tight_layout()
plt.show()

To color each bar differently, pass a list:

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]
colors     = ['#e74c3c', '#3498db', '#2ecc71', '#f39c12', '#9b59b6']

plt.bar(categories, values, color=colors)

plt.title('Multi-Color Bar Chart')
plt.xlabel('Category')
plt.ylabel('Value')
plt.tight_layout()
plt.show()

Bar Width and Edge Color

The default width is 0.8 (80 % of the space between tick positions). Decrease it for a lighter look or increase it to fill more space. edgecolor draws an outline around each bar:

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]

plt.bar(
    categories, values,
    width=0.5,
    color='cornflowerblue',
    edgecolor='navy',
    linewidth=1.2,
)

plt.title('Custom Width and Edge')
plt.xlabel('Category')
plt.ylabel('Value')
plt.tight_layout()
plt.show()

Adding a Legend

Pass a label string to bar() and then call plt.legend():

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]

plt.bar(categories, values, color='teal', label='2024 Sales')
plt.legend()

plt.title('Bar Chart with Legend')
plt.xlabel('Category')
plt.ylabel('Value')
plt.tight_layout()
plt.show()

Rotating Tick Labels

Long category names overlap unless you rotate them with plt.xticks(rotation=...):

import matplotlib.pyplot as plt

months = ['January', 'February', 'March', 'April', 'May', 'June']
sales  = [120, 95, 140, 160, 130, 175]

plt.bar(months, sales, color='darkorange')
plt.xticks(rotation=45, ha='right')   # ha='right' aligns the labels nicely

plt.title('Monthly Sales')
plt.xlabel('Month')
plt.ylabel('Units Sold')
plt.tight_layout()
plt.show()

Horizontal Bar Charts

plt.barh(y, width) draws bars that extend horizontally. It is particularly useful when category labels are long, because they appear on the y-axis with plenty of horizontal space.

import matplotlib.pyplot as plt

languages  = ['Python', 'JavaScript', 'Java', 'C#', 'TypeScript']
popularity = [30.3, 25.1, 17.8, 12.4, 9.6]

plt.barh(languages, popularity, color='mediumseagreen')

plt.title('Programming Language Popularity (%)')
plt.xlabel('Share (%)')
plt.ylabel('Language')
plt.tight_layout()
plt.show()

Tip: sort the data before plotting so the most-popular category appears at the top:

import matplotlib.pyplot as plt

languages  = ['Python', 'JavaScript', 'Java', 'C#', 'TypeScript']
popularity = [30.3, 25.1, 17.8, 12.4, 9.6]

# Sort ascending so the highest bar ends up at the top after barh reversal
pairs      = sorted(zip(popularity, languages))
popularity_sorted, languages_sorted = zip(*pairs)

plt.barh(languages_sorted, popularity_sorted, color='mediumseagreen')

plt.title('Programming Language Popularity (sorted)')
plt.xlabel('Share (%)')
plt.tight_layout()
plt.show()

Grouped Bar Charts

A grouped (side-by-side) bar chart compares two or more series for the same set of categories. Matplotlib does not have a dedicated function for this — you create it by shifting the x positions of each series using NumPy:

import matplotlib.pyplot as plt
import numpy as np

categories = ['Q1', 'Q2', 'Q3', 'Q4']
sales_2023 = [120, 135, 150, 170]
sales_2024 = [140, 160, 145, 195]

x     = np.arange(len(categories))  # [0, 1, 2, 3]
width = 0.35                         # width of each individual bar

fig, ax = plt.subplots()

bars1 = ax.bar(x - width / 2, sales_2023, width, label='2023', color='steelblue')
bars2 = ax.bar(x + width / 2, sales_2024, width, label='2024', color='darkorange')

ax.set_title('Quarterly Sales Comparison')
ax.set_xlabel('Quarter')
ax.set_ylabel('Units Sold')
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend()

plt.tight_layout()
plt.show()

Key points:

  • x - width / 2 shifts the first series left; x + width / 2 shifts the second series right, so the bars sit side by side.
  • ax.set_xticks(x) and ax.set_xticklabels(categories) place the category labels at the center of each group.
  • The object-oriented API (fig, ax = plt.subplots()) is preferred for grouped charts because it gives you explicit control over each axis.

Adding Value Labels on Bars

You can annotate each bar with its value using ax.bar_label() (added in Matplotlib 3.4):

import matplotlib.pyplot as plt
import numpy as np

categories = ['Q1', 'Q2', 'Q3', 'Q4']
sales_2023 = [120, 135, 150, 170]
sales_2024 = [140, 160, 145, 195]

x     = np.arange(len(categories))
width = 0.35

fig, ax = plt.subplots()
bars1 = ax.bar(x - width / 2, sales_2023, width, label='2023', color='steelblue')
bars2 = ax.bar(x + width / 2, sales_2024, width, label='2024', color='darkorange')

ax.bar_label(bars1, padding=3)
ax.bar_label(bars2, padding=3)

ax.set_title('Quarterly Sales with Labels')
ax.set_xlabel('Quarter')
ax.set_ylabel('Units Sold')
ax.set_xticks(x)
ax.set_xticklabels(categories)
ax.legend()

plt.tight_layout()
plt.show()

Stacked Bar Charts

A stacked bar chart places series on top of each other, showing how individual parts contribute to a total. Use the bottom parameter to tell Matplotlib where each series should start:

import matplotlib.pyplot as plt
import numpy as np

categories = ['A', 'B', 'C', 'D', 'E']
series1    = [10, 24, 36, 40, 15]
series2    = [ 5, 12, 15, 20, 10]
series3    = [ 8,  6, 10, 12,  7]

x = np.arange(len(categories))

plt.bar(x, series1, label='Group 1', color='steelblue')
plt.bar(x, series2, label='Group 2', color='darkorange',
        bottom=series1)
plt.bar(x, series3, label='Group 3', color='seagreen',
        bottom=np.array(series1) + np.array(series2))

plt.xticks(x, categories)
plt.title('Stacked Bar Chart')
plt.xlabel('Category')
plt.ylabel('Total Value')
plt.legend()
plt.tight_layout()
plt.show()

The bottom for the third series is the element-wise sum of the first two, so each bar starts exactly where the previous one ended. Using np.array() makes the addition work correctly for lists.

Error Bars

When your data has uncertainty or variability (e.g., standard deviation across repeated measurements), add error bars with the yerr parameter:

import matplotlib.pyplot as plt
import numpy as np

categories    = ['Control', 'Treatment A', 'Treatment B', 'Treatment C']
means         = [5.2, 7.8, 6.1, 9.4]
std_devs      = [0.5, 0.8, 0.6, 1.1]

plt.bar(
    categories, means,
    yerr=std_devs,
    capsize=5,           # width of the error bar caps
    color='cornflowerblue',
    edgecolor='black',
    linewidth=0.8,
)

plt.title('Experimental Results with Error Bars')
plt.xlabel('Group')
plt.ylabel('Mean Value')
plt.tight_layout()
plt.show()

capsize controls the horizontal caps at the top and bottom of each error bar; a value of 4–6 is usually readable.

Saving a Bar Chart to a File

Use plt.savefig() instead of plt.show() (or call it before show()). You can specify the format via the file extension:

import matplotlib.pyplot as plt

categories = ['A', 'B', 'C', 'D', 'E']
values     = [10, 24, 36, 40, 15]

plt.bar(categories, values, color='steelblue')
plt.title('Saved Bar Chart')
plt.xlabel('Category')
plt.ylabel('Value')
plt.tight_layout()

# Save as PNG at 150 dpi
plt.savefig('bar_chart.png', dpi=150)

# Save as vector PDF (ideal for publications)
plt.savefig('bar_chart.pdf')

plt.show()

Common format options: png, pdf, svg, eps. Use svg or pdf when you need a scalable, print-ready image.

Controlling Figure Size

By default Matplotlib creates a 6.4 × 4.8 inch figure. Pass figsize=(width, height) to plt.figure() or plt.subplots() to override it:

import matplotlib.pyplot as plt

categories = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
              'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']
values     = [80, 70, 95, 110, 130, 150, 160, 155, 120, 100, 85, 90]

fig, ax = plt.subplots(figsize=(12, 5))  # wider figure for 12 months
ax.bar(categories, values, color='tomato')
ax.set_title('Monthly Data')
ax.set_xlabel('Month')
ax.set_ylabel('Value')

plt.tight_layout()
plt.savefig('monthly_bar_chart.png', dpi=150)
plt.show()

Quick Reference

TaskCode
Vertical bar chartplt.bar(x, y)
Horizontal bar chartplt.barh(y, width)
Change colorplt.bar(x, y, color='steelblue')
Change widthplt.bar(x, y, width=0.5)
Stacked barsplt.bar(x, y2, bottom=y1)
Error barsplt.bar(x, y, yerr=errors, capsize=5)
Add value labelsax.bar_label(bars, padding=3)
Rotate x labelsplt.xticks(rotation=45, ha='right')
Save to fileplt.savefig('file.png', dpi=150)
Was this page helpful?