HOWTO · Matplotlib

How to Plot Histogram for List of Data in Matplotlib

Plot a histogram from a Python list with Matplotlib's plt.hist(), then adjust its bins, density, and color.

On this page

To plot a histogram from a Python list, pass the list directly to Matplotlib’s plt.hist() function. The function groups the values into intervals called bins, counts how many values fall into each interval, and draws one bar per interval, so the shape of the chart shows how the values are distributed. A plain Python list works as the input; you do not need to convert it to a NumPy array or a pandas Series first. This article plots one list of 21 whole numbers from 0 to 9, then adjusts the bins, the bar heights, and the bar color, and finally shows what happens with an invalid bin count and an empty list.

Plot a histogram from a list with plt.hist()

With no extra arguments, plt.hist() divides the data range into ten equal-width bins. The example also captures the three values the function returns and prints them, so you can see exactly how the list was grouped:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data)
print("counts:", counts.tolist())
print("bin_edges:", bin_edges.tolist())
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram of data")
plt.show()
counts: [1.0, 1.0, 2.0, 2.0, 5.0, 3.0, 3.0, 2.0, 1.0, 1.0]
bin_edges: [0.0, 0.9, 1.8, 2.7, 3.6, 4.5, 5.4, 6.3, 7.2, 8.1, 9.0]

Histogram of the 21 list values drawn by plt.hist() with the default ten bins; the tallest bar covers values from 3.6 to 4.5 and has a count of 5.

plt.hist() returns three values. counts holds the number of observations in each bin, bin_edges gives the interval boundaries, and patches contains the bar artists if you need to restyle individual bars afterwards. The number of entries in counts is the number of bins, while bin_edges always has one more entry because it marks both ends of every interval. Here the data range is 0 to 9, so each of the ten default bins is 0.9 wide, and the tallest bar is the interval from 3.6 to 4.5, which contains the five occurrences of the value 4. You can omit the assignment and call plt.hist(data) on its own if you only need the chart.

Set the number of bins or the bin edges

The bins parameter controls how the data is divided. Passing an integer such as bins=20 creates that many equal-width intervals across the data range:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=20)
print("counts:", counts.tolist())
print("number of bins:", len(counts))
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Histogram with 20 bins")
plt.show()
counts: [1.0, 0.0, 1.0, 0.0, 2.0, 0.0, 2.0, 0.0, 5.0, 0.0, 0.0, 3.0, 0.0, 3.0, 0.0, 2.0, 0.0, 1.0, 0.0, 1.0]
number of bins: 20

The same list plotted with 20 bins; many of the narrow bins stay empty because the data holds only whole numbers from 0 to 9.

Twenty bins are too many for this small list: because the values are whole numbers, every second bin stays empty and the distribution looks noisier than it is. In general, very few bins can hide structure in the data, while many narrow bins can make a small dataset look ragged, so choose a bin count that matches the size and spread of your list. Instead of an integer, you can pass a sequence of edge values such as bins=[0, 2, 4, 6, 8, 10] to set the interval boundaries yourself; the intervals can then have different widths. When the counts span a wide range, the optional log=True argument switches the count axis to a logarithmic scale so smaller bars stay visible next to a tall one. It changes only the axis scale, not the input values or the binning.

Show probability density instead of counts

By default, bar heights are counts. Set density=True when you want a probability density instead. Matplotlib then scales the heights so that the total area of the bars is 1:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
counts, bin_edges, patches = plt.hist(data, bins=10, density=True)
print("densities:", [round(d, 4) for d in counts.tolist()])
area = sum(d * (bin_edges[i + 1] - bin_edges[i]) for i, d in enumerate(counts))
print("total area:", round(area, 6))
plt.xlabel("Value")
plt.ylabel("Probability density")
plt.title("Probability histogram of data")
plt.show()
densities: [0.0529, 0.0529, 0.1058, 0.1058, 0.2646, 0.1587, 0.1587, 0.1058, 0.0529, 0.0529]
total area: 1.0

Probability density histogram of the list drawn with density=True; the bar heights are densities and the total area of the bars is 1.

With density=True, the first value in the returned tuple holds densities rather than counts, and the example verifies the normalization by multiplying each density by its bin width and summing the areas, which gives exactly 1. Note that an individual bar height is a density, not the probability of its interval; the probability of an interval is its bar area, meaning height multiplied by width. This distinction matters when the bins have different widths, because a wide bar can represent more probability than a taller narrow one. Remember to update the vertical-axis label when you switch, as the example does, so the chart explains what its bar heights mean.

Change the bar color

The color parameter sets the fill color of the bars:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=10, color="red")
plt.xlabel("Value")
plt.ylabel("Count")
plt.title("Red histogram of data")
plt.show()

The list histogram with red bars, drawn with ten bins and the color parameter set to red.

Any Matplotlib color specification works, such as a name like "red", a hex string like "#1f77b4", or a single-letter code. The display options combine freely in one call, for example plt.hist(data, bins=20, density=True, color="red") draws twenty red bars with density heights. Whichever options you combine, keep the title and axis labels aligned with them: a density plot should not keep a count label, and a chart with twenty bins should not claim to show the default ten.

Handle an invalid bin count and an empty list

Two input mistakes are worth knowing about before they surprise you. The first is a bin count that is not positive:

import matplotlib.pyplot as plt

data = [3, 4, 2, 3, 4, 5, 4, 7, 8, 5, 4, 6, 2, 1, 0, 9, 7, 6, 6, 5, 4]
plt.hist(data, bins=0)
Traceback (most recent call last):
  ...
ValueError: `bins` must be positive, when an integer

bins=0 (and any negative integer) raises a ValueError and nothing is drawn. If the bin count comes from user input or from a calculation, validate that it is a positive integer before plotting, or pass a sequence of edges instead. The second case is an empty list, which behaves very differently: it is not an error at all.

import matplotlib.pyplot as plt

counts, bin_edges, patches = plt.hist([])
print("counts:", counts.tolist())
print("bin_edges:", [round(e, 2) for e in bin_edges.tolist()])
counts: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]
bin_edges: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]

With no data, plt.hist() falls back to the default range from 0 to 1, returns ten zero counts, and draws an empty set of axes without raising. That makes an empty list a silent failure mode: if your data comes from a file, a query, or a filter, check that the list is non-empty before plotting, so a missing dataset does not turn into a blank chart that looks like a valid result.