Lesson 17
Choosing Histogram Bins
Set the number of bins or their exact edges, work out the bin width, and see how the choice changes the shape a histogram shows.
The bins are the one real decision a histogram asks you to make, and they change what the chart says. Too few and every interesting feature is flattened into one block. Too many and the shape disappears into noise. You can give bins a count and let Matplotlib divide the range, or hand it the exact edges.
A count, and the width it implies
Give bins a whole number and Matplotlib splits the range from the smallest value to the largest into that many equal intervals. The width follows from arithmetic you can do yourself: the range divided by the number of bins.
import matplotlib.pyplot as plt
heights = [
189, 185, 195, 149, 189, 147, 154, 174, 169, 195, 159, 192,
155, 191, 153, 157, 140, 144, 172, 157, 181, 182, 166, 167,
]
counts, edges, _ = plt.hist(heights, bins=5, edgecolor="#0F172A")
plt.title("Five equal bins")
plt.xlabel("centimetres")
plt.ylabel("people")
plt.show()
print("smallest:", min(heights), "largest:", max(heights))
print("width by hand:", (max(heights) - min(heights)) / 5)
print("width Matplotlib used:", round(edges[1] - edges[0], 1))Output:
smallest: 140 largest: 195
width by hand: 11.0
width Matplotlib used: 11.0The same data, three bin counts
Nothing about the data changes here, only the number of bins, and the story changes with it. This is worth doing every time: try a few counts, and only believe a feature that survives all of them.
import matplotlib.pyplot as plt
values = [
87, 53, 66, 61, 67, 68, 62, 110, 104, 61, 111, 123, 117, 119,
116, 104, 92, 111, 90, 103, 81, 80, 101, 51, 79, 107, 110, 129,
145, 139, 110,
]
plt.figure(figsize=(11, 4))
for index, count in enumerate([3, 7, 20], start=1):
plt.subplot(1, 3, index)
plt.hist(values, bins=count, edgecolor="#0F172A", color="#2DD4BF")
plt.title(f"bins={count}")
plt.tight_layout()
plt.show()
print("range:", max(values) - min(values))
for count in [3, 7, 20]:
print(f"bins={count:<3} width {(max(values) - min(values)) / count:.1f}")Output:
range: 94
bins=3 width 31.3
bins=7 width 13.4
bins=20 width 4.7Three bins hide the two clusters. Twenty turns them into spikes that mostly reflect which single values happened to appear. Seven shows the shape that is really there.
Your own edges
Pass a list and its numbers become the boundaries. Five edges make four bins. Every bin is half-open, including its left edge and excluding its right, so a value of exactly 150 lands in the bin starting at 150. The last bin is the exception: it includes both ends, or the largest value would be counted nowhere.
import matplotlib.pyplot as plt
marks = [30, 34, 40, 44, 50, 55, 60, 60, 70, 84, 90, 100]
grades = [0, 35, 50, 70, 100]
counts, edges, _ = plt.hist(marks, bins=grades, edgecolor="#0F172A", color="#7C3AED")
plt.xticks(grades)
plt.title("Bins chosen to match the grade boundaries")
plt.xlabel("mark")
plt.ylabel("learners")
plt.show()
for start, end, count in zip(edges, edges[1:], counts):
print(f"[{start:g}, {end:g}) -> {int(count)}")
print("the mark of exactly 100 is counted in the last bin")Output:
[0, 35) -> 2
[35, 50) -> 2
[50, 70) -> 4
[70, 100) -> 4
the mark of exactly 100 is counted in the last binUnequal widths, read with care
Your edges do not have to be evenly spaced, which is useful when the meaningful groups are uneven. Be careful reading the result: a wide bin catches more values simply for being wide, so its taller bar is not necessarily a denser range.
import matplotlib.pyplot as plt
heights = [
189, 185, 195, 149, 189, 147, 154, 174, 169, 195, 159, 192,
155, 191, 153, 157, 140, 144, 172, 157, 181, 182, 166, 167,
]
edges = [140, 150, 160, 175, 185, 200]
counts, used, _ = plt.hist(heights, bins=edges, edgecolor="#0F172A", color="#94A3B8")
plt.xticks(edges)
plt.title("Bins of different widths")
plt.xlabel("centimetres")
plt.ylabel("people")
plt.show()
for start, end, count in zip(used, used[1:], counts):
width = end - start
print(f"[{start:g}, {end:g}) width {width:g} count {int(count)} "
f"per cm {count / width:.2f}")Output:
[140, 150) width 10 count 4 per cm 0.40
[150, 160) width 10 count 6 per cm 0.60
[160, 175) width 15 count 5 per cm 0.33
[175, 185) width 10 count 2 per cm 0.20
[185, 200) width 15 count 7 per cm 0.47The last column is the honest comparison. Dividing each count by its bin width shows which range is genuinely crowded rather than simply wide.
Key takeaways
-
bins as a number splits the range into that many equal intervals
-
Bin width is the range divided by the number of bins
-
bins as a list gives the exact edges, and n edges make n minus 1 bins
-
Bins include their left edge and exclude their right, except the last
-
Next: Histogram Styles
-
Previous: Histograms
Loading playground…
Check your understanding
Choosing Histogram Bins Quiz
What does bins=6 do?
1 / 10