What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides 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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

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.

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:

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
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.

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.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

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.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Version and compatibility notes

  • Brace-style formatting in fmt and 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.

“

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.