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 matchPC Slower Than It Used to Be?
A free scan shows the junk files, broken settings and background clutter dragging Windows down - then fixes them in one click.Free scan · Windows 10 & 11iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more
To put values on a bar chart, pass the result of ax.bar() to ax.bar_label(). To attach a note to an individual scatter point, call ax.annotate() with the point’s coordinates as xy and the note text as the first argument. Both calls are part of Matplotlib’s standard API, so no extra package is needed. The rest of this guide covers the options that change how the labels look and where they land, and the cases where a different call is the better choice.
Labeling bars with bar_label
Matplotlib’s bar-chart reference recommends bar_label for putting labels on bars. The method takes the BarContainer that ax.bar() returns, so the bars must be captured in a variable first.
The basic pattern
import matplotlib.pyplot as plt
categories = ["North", "South", "East", "West"]
values = [12.4, 8.9, 15.1, 10.2]
fig, ax = plt.subplots()
bars = ax.bar(categories, values)
ax.bar_label(bars, padding=3, fmt="{:.1f}")
plt.show()
ax.bar() returns a BarContainer, and bar_label() places one text label per bar. The method returns a list of Annotation objects, which you can keep if you need to restyle or move individual labels later.
Free tools Windows power users keep installed
One-click scans. No signup required.
Controlling the number format
The fmt argument controls how each numeric value is rendered. It accepts percent-style format strings, and since Matplotlib 3.7 it also accepts brace-style strings such as "{:.1f}" and callables. If your installation is older than 3.7, use a percent-style string such as "%.1f" instead. The padding argument sets the gap between the bar end and the label, measured in points. In the stable reference labelled 3.11.2, padding also accepts a per-label array, so each bar can get a different gap.
#1 Best Overall
To show text that is not the raw value, pass labels with one string per bar:
ax.bar_label(bars, labels=["12 k", "9 k", "15 k", "10 k"], padding=3)
Stacked bars and the label_type setting
The label_type argument decides which number a label reports. The default, 'edge', places the label at the end of each bar segment, so for a stacked bar the label shows the running top of the stack rather than the segment’s own size. Set label_type='center' when you want each segment’s length, and place the label in the middle of that segment:
Rank #2
fig, ax = plt.subplots()
base = ax.bar(categories, [4, 6, 3, 5], label="Q1")
top = ax.bar(categories, [2, 3, 4, 2], bottom=[4, 6, 3, 5], label="Q2")
ax.bar_label(base, label_type="center")
ax.bar_label(top, label_type="center")
Alignment, clipping, and axis limits
bar_label sets its own alignment so each label sits correctly against its bar, which means horizontal and vertical alignment keyword arguments are not supported by this helper. Other styling keywords, such as fontsize and color, are passed through to the underlying text object. If a label on the tallest bar is cut off at the top of the plot, or runs past the edge, raise the upper y-limit before you save the figure:
Quick wins for a faster PC:
Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Clear out junk files and repair common Windows errorsFree Scan →Scan for outdated or missing drivers - takes under a minuteDriver Scan →ax.set_ylim(0, max(values) * 1.15)
Check the rendered figure after changing limits, because a label that is clipped on screen can still look fine in a notebook preview at a different size.
Annotating scatter points with annotate
A scatter plot has no container object, so you attach labels to coordinates one at a time. ax.annotate(text, xy=(x, y), ...) does this. The xy argument is the point being described, and xytext is where the text is drawn. The two are independent, which is what lets a label sit beside its point rather than on top of it.
Offset labels next to each point
import matplotlib.pyplot as plt
x = [1.2, 2.5, 3.1, 4.8]
y = [3.4, 1.9, 4.6, 2.7]
labels = ["A", "B", "C", "D"]
fig, ax = plt.subplots()
ax.scatter(x, y)
for xi, yi, label in zip(x, y, labels):
ax.annotate(label, xy=(xi, yi), xytext=(4, 4),
textcoords="offset points")
plt.show()
Setting textcoords="offset points" means xytext=(4, 4) is a fixed shift of 4 points right and 4 points up from the point. Because the shift is in points rather than data units, the gap stays the same when you zoom or resize the figure. Note that the offset is relative to the point, not to the axes.
Rank #4
Adding an arrow
When a label sits far from its point, or several points are close together, add arrowprops to draw a line from the text to the target:
ax.annotate("outlier", xy=(4.8, 2.7), xytext=(3.5, 0.5),
arrowprops=dict(arrowstyle="->"))
Without arrowprops, the annotation is text only. Keep labels short and label only the points that matter. In a dense scatter plot, labelling every point turns the chart into overlapping text, and no placement setting fixes that. Choosing which points to label is a design decision, and the API cannot make it for you.
Best Value
Placing a free-standing note
If the note is not tied to any particular point, ax.text(x, y, "note", fontsize=12) is simpler. The method takes a position in data coordinates and draws the text there. Use annotate instead when the note needs to stay linked to a point, or when you want an arrow.
Choosing between bar_label, text, and annotate
Three methods cover most cases. The right one depends on what the data are and how the text relates to them.
| Situation | Use | Why |
|---|---|---|
Labels on bars returned by ax.bar() |
ax.bar_label(container) |
Generates labels from the bar values, supports fmt, labels, and label_type |
| Labels on individual scatter or line points | ax.annotate(text, xy=...) |
Ties the text to a data point, with offset and optional arrow |
| A word or note at a fixed position on the axes | ax.text(x, y, text) |
Simple placement with no link to a point |
The Matplotlib text introduction describes text as the basic method for adding text at an Axes location. The annotations guide describes annotate as the more flexible option, since it supports separate target and text positions and an optional arrow.
Version and compatibility notes
- Brace-style formatting in
fmtand callable formatters require Matplotlib 3.7 or later. - Per-label padding arrays are described in the stable reference labelled 3.11.2. Verify this against your installed version before relying on it.
- Check your version with
python -c "import matplotlib; print(matplotlib.__version__)".
The examples above use only bar, bar_label, scatter, annotate, and text, which have been part of Matplotlib for a long time, so they work on recent releases without changes. Only the formatting and padding options noted above depend on newer versions.
Reference documentation
The official pages behind this guide are the bar_label reference, the Axes.bar reference, the annotations guide, and the introduction to text in Matplotlib. The Axes API page lists every method on the object you are calling.
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.

