Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallTo label bars, pass the container returned by ax.bar() to ax.bar_label(). To label scatter points, loop over your coordinates and call ax.annotate() with a target xy and an offset xytext. Both take only a few lines. The choices that matter are how the numbers are formatted, where the text sits, and whether a note must stay attached to a data point.
Label bars with bar_label
ax.bar() returns a BarContainer, and ax.bar_label() places one text label per bar in that container. The Axes.bar reference recommends this helper for bar labels, and the bar_label reference documents the options below.
As an Amazon Associate I earn from qualifying purchases.
Basic value labels
The following example writes each value above its bar, rounded to one decimal place:
import matplotlib.pyplot as plt
categories = ["Apples", "Pears", "Plums"]
values = [12.5, 8.4, 15.0]
fig, ax = plt.subplots()
bars = ax.bar(categories, values)
ax.bar_label(bars, padding=3, fmt="{:.1f}")
plt.show()
padding is measured in points. bar_label returns a list of Annotation objects, so you can keep that return value if you need to restyle or remove individual labels later.
#1 Best Overall
Formatting values
The fmt argument accepts three forms:
- Percent-style strings, such as
"%.1f", which work in every release. - Brace-style strings, such as
"{:,.0f}"for thousands separators, which the reference lists as added in Matplotlib 3.7. - Callables that take a value and return a string, also added in Matplotlib 3.7.
If you need text that is not derived from the bar height, pass labels instead. Each entry is used as-is:
bars = ax.bar(categories, values)
ax.bar_label(bars, labels=[f"{v}%" for v in values], padding=3)
Stacked bars and label_type
The label_type argument decides which point of each segment is used:
Rank #2
label_type="edge"(the default) labels the segment endpoint. On a stacked bar, the number shown therefore reflects the position of that edge, not the segment’s own length.label_type="center"reports the length of each segment, which is usually what you want when a stacked bar represents parts of a total.
import matplotlib.pyplot as plt
categories = ["Q1", "Q2", "Q3"]
part_a = [3, 2, 4]
part_b = [1, 5, 2]
fig, ax = plt.subplots()
bars_a = ax.bar(categories, part_a)
bars_b = ax.bar(categories, part_b, bottom=part_a)
ax.bar_label(bars_a, label_type="center")
ax.bar_label(bars_b, label_type="center")
plt.show()
Bar labels are aligned automatically. The helper does not accept horizontal or vertical alignment keywords, so if you need custom alignment, use annotate instead.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Labels that get clipped
Labels placed above the tallest bar can run past the top of the axes. Add headroom before you save the figure:
ax.set_ylim(0, max(values) * 1.15)
Then inspect the rendered figure, because the exact amount of space depends on your font size and figure dimensions. The bar_label reference notes that other styling keywords are passed through to annotate, so you can set properties such as fontsize in the same call.
Annotate scatter points
A scatter plot does not have a bar container, so each label must be placed individually. The Annotations guide describes annotate as text that explains, adds context to, or highlights part of a visualization. The core call is ax.annotate(text, xy=(x, y), ...), where xy is the point being labeled.
Rank #4
import matplotlib.pyplot as plt
x = [1, 2, 3, 4]
y = [2.5, 3.1, 1.8, 4.0]
names = ["A", "B", "C", "D"]
fig, ax = plt.subplots()
ax.scatter(x, y)
for xi, yi, name in zip(x, y, names):
ax.annotate(name, xy=(xi, yi), xytext=(4, 4),
textcoords="offset points")
plt.show()
Target and text positions
The point being labeled (xy) and the displayed text position (xytext) are separate coordinates. Setting textcoords="offset points" makes xytext a fixed offset from the point, measured in points. A label therefore stays next to its marker when you change the axis limits or resize the figure, because its offset does not depend on the data scale.
Adding an arrow
If the text sits far enough from its point that the link is unclear, add arrowprops:
Best Value
ax.annotate(name, xy=(xi, yi), xytext=(30, 25),
textcoords="offset points",
arrowprops=dict(arrowstyle="->"))
Label only the points that matter. The API documents the placement mechanics, but how many labels a dense scatter plot can carry before it becomes hard to read is a design decision for you to make.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Choosing between bar_label, annotate, and text
The three methods overlap, so the choice usually comes down to what the text belongs to.
| Method | Best used for | Anchored to | Arrow support | Notable options |
|---|---|---|---|---|
ax.bar_label(container) |
Values on bars returned by ax.bar() |
Each bar or stacked segment (label_type) |
Not described in the bar_label reference | fmt, labels, padding, label_type |
ax.annotate(text, xy=...) |
Labels tied to individual data points | A data point xy, with text at xytext |
Yes, through arrowprops |
xytext, textcoords, arrowprops, text properties |
ax.text(x, y, text) |
Free-standing words or notes at a fixed position | The given x, y location, with no point relationship |
Not stated in the Text in Matplotlib guide | Text properties such as fontsize |
In practice, use bar_label whenever the labels come from bar heights, use annotate when a label must stay with a point or needs a pointer, and use text for a note at a known location. The Text in Matplotlib guide covers the text object itself. For how figures and Axes relate, including why fig, ax = plt.subplots() returns both, see the Introduction to Axes page and the matplotlib.axes API reference.
Version notes
The current stable documentation is labeled version 3.11.2. Brace-style fmt strings and callables require Matplotlib 3.7 or later, so if an example fails with a formatting error, check your installed version:
python -c "import matplotlib; print(matplotlib.__version__)"
The stable reference also accepts a per-label array for padding, which is useful when some labels need more clearance than others. Use a single number on older installations.
Quick Recap
“
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




