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]
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
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
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()
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.