How to Plot Error Bars in Matplotlib (plt.errorbar)

To plot error bars in Matplotlib, use plt.errorbar(x, y, yerr=errors); add xerr for horizontal error bars and capsize for the little caps. For a bar chart, pass yerr to plt.bar(). This guide covers plt.errorbar step by step: styling, error bars without a connecting line, asymmetric error bars, calculating the error (standard deviation or standard error) with pandas, bar charts, dates on the x-axis, and shaded error bands.

Tested with Python 3.12.5, Matplotlib 3.11.2, NumPy 2.5.3 and pandas 3.0.6. The charts are real Matplotlib windows and the table comes from the Windows Command Prompt. Reference: matplotlib.pyplot.errorbar.

Basic error bars with plt.errorbar()

import matplotlib.pyplot as plt

doses = [0, 10, 20, 30, 40, 50]
response = [2.1, 3.8, 5.9, 7.2, 8.1, 8.6]
error = [0.4, 0.5, 0.6, 0.5, 0.7, 0.6]

plt.errorbar(doses, response, yerr=error, capsize=4, marker="o")
plt.xlabel("Dose (mg)")
plt.ylabel("Response")
plt.title("plt.errorbar with vertical error bars")
plt.show()
Matplotlib line chart of dose against response with vertical error bars and caps drawn with plt.errorbar
yerr draws a vertical bar of ± error at each point.
ArgumentWhat it does
yerr, xerrError size: one number for all points, one per point, or [lower, upper]
fmtMarker and line format, e.g. "o" (markers only) or "-o"
capsize, capthickLength and thickness of the caps
ecolor, elinewidthColour and width of the error bars
erroreveryDraw an error bar only on every n-th point
uplims, lolimsShow arrows for upper or lower limits

x and y error bars without a line

fmt="o" (or any marker without a line style) draws only the markers and the error bars, like a scatter plot with error bars:

import matplotlib.pyplot as plt
import numpy as np

rng = np.random.default_rng(4)
x = np.arange(1, 9)
y = 2 * x + rng.normal(0, 1.5, 8)
xerr = rng.uniform(0.2, 0.5, 8)
yerr = rng.uniform(0.8, 2.0, 8)

plt.errorbar(x, y, xerr=xerr, yerr=yerr,
             fmt="s",                      # markers only: no connecting line
             color="darkblue", ecolor="gray", elinewidth=1.5, capsize=5, capthick=1.5, markersize=6)
plt.title("x and y error bars, fmt='s' (no line)")
plt.grid(alpha=0.3)
plt.show()
Matplotlib scatter-style plot with square markers, gray horizontal and vertical error bars and caps, without a connecting line
xerr + yerr with fmt="s": no line between the points.

Asymmetric error bars

When the error is not the same above and below the point, pass a list of two sequences: the distances below and the distances above:

import matplotlib.pyplot as plt

x = [1, 2, 3, 4, 5]
y = [10, 14, 12, 18, 16]
lower = [1, 2, 1.5, 3, 1]          # distance below each point
upper = [3, 1, 4, 2, 5]            # distance above each point

plt.errorbar(x, y, yerr=[lower, upper], fmt="o", capsize=5, color="tab:red")
plt.title("Asymmetric error bars: yerr=[lower, upper]")
plt.show()
Matplotlib plot with asymmetric red error bars that extend different distances below and above each point
yerr=[lower, upper].

Both lists contain positive distances, not the end values. If you have lower and upper bounds (for example a confidence interval), convert them first: lower = y - lo, upper = hi - y.

For more examples, see asymmetric error bars in Matplotlib.

Calculate the error: standard deviation or standard error

Error bars usually show the standard deviation (spread of the data) or the standard error of the mean (SEM, uncertainty of the mean). With pandas you can get both per group:

import numpy as np
import pandas as pd

rng = np.random.default_rng(10)
df = pd.DataFrame({
    "group": np.repeat(["Control", "Drug A", "Drug B"], 12),
    "score": np.concatenate([rng.normal(50, 6, 12), rng.normal(58, 8, 12), rng.normal(63, 5, 12)]),
})

summary = df.groupby("group")["score"].agg(["mean", "std", "count"])
summary["sem"] = summary["std"] / np.sqrt(summary["count"])      # standard error of the mean
print(summary.round(2))

Output:

          mean   std  count   sem
group                            
Control  48.85  4.11     12  1.19
Drug A   53.99  6.05     12  1.75
Drug B   62.89  5.22     12  1.51
Command Prompt output of a pandas groupby table with the mean, standard deviation, count and standard error of the mean for three groups
Mean, standard deviation and SEM per group.

Always say in the axis label or caption which one you plot: SEM bars are much shorter than standard-deviation bars for the same data.

Bar chart with error bars

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd

rng = np.random.default_rng(10)
df = pd.DataFrame({
    "group": np.repeat(["Control", "Drug A", "Drug B"], 12),
    "score": np.concatenate([rng.normal(50, 6, 12), rng.normal(58, 8, 12), rng.normal(63, 5, 12)]),
})
summary = df.groupby("group")["score"].agg(["mean", "std", "count"])
summary["sem"] = summary["std"] / np.sqrt(summary["count"])

fig, ax = plt.subplots(figsize=(6, 4.5))
ax.bar(summary.index, summary["mean"], yerr=summary["sem"], capsize=8,
       color=["gray", "tab:blue", "tab:green"], edgecolor="black")      # bar chart with error bars
ax.set_ylabel("Mean score (± SEM)")
plt.show()
Matplotlib bar chart of mean scores for Control, Drug A and Drug B with black-edged bars and SEM error bars with caps
ax.bar(..., yerr=summary["sem"], capsize=8).

Error bars with dates on the x-axis

errorbar() accepts datetime values for x, so time series with uncertainty work the same way:

import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import pandas as pd

dates = pd.date_range("2025-01-01", periods=8, freq="W")
temperature = [3.1, 4.0, 2.5, 5.2, 6.8, 6.1, 8.4, 9.0]
spread = [1.2, 0.9, 1.5, 1.1, 1.4, 0.8, 1.0, 1.3]

fig, ax = plt.subplots(figsize=(8, 4))
ax.errorbar(dates, temperature, yerr=spread, fmt="-o", capsize=4)      # dates work as x values
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d %b"))
ax.set_ylabel("Weekly mean temperature (°C)")
fig.autofmt_xdate()
plt.show()
Matplotlib time series of weekly mean temperature with error bars and date labels on the x-axis
Weekly dates on the x-axis, formatted as day and month.

Many points: errorevery and shaded error bands

With dense data, a bar at every point is hard to read. Draw one every n points with errorevery, or show the error as a shaded band with fill_between():

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 50)
y = np.sin(x) + x / 3
err = 0.2 + 0.05 * x

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4))
ax1.errorbar(x, y, yerr=err, errorevery=5, capsize=3)                 # a bar on every 5th point
ax1.set_title("errorevery=5")
ax2.plot(x, y)
ax2.fill_between(x, y - err, y + err, alpha=0.3, label="± error")    # shaded error band
ax2.set_title("fill_between error band")
ax2.legend()
plt.tight_layout()
plt.show()
Two Matplotlib plots: error bars drawn on every fifth point with errorevery, and a shaded error band made with fill_between
errorevery=5 (left) and a fill_between band (right).

Error bars for several series

Shift each series slightly on the x-axis so the error bars do not hide each other:

import matplotlib.pyplot as plt
import numpy as np

weeks = np.arange(1, 7)
series = {"Model A": ([70, 74, 77, 81, 83, 86], [3, 3, 2, 2, 3, 2]),
          "Model B": ([68, 71, 76, 78, 84, 88], [4, 3, 3, 4, 3, 3])}

fig, ax = plt.subplots(figsize=(7, 4.5))
for shift, (name, (mean, err)) in zip([-0.1, 0.1], series.items()):
    ax.errorbar(weeks + shift, mean, yerr=err, fmt="o-", capsize=4, label=name)   # shift so bars do not overlap
ax.set_xlabel("Week")
ax.set_ylabel("Accuracy (%)")
ax.legend()
plt.show()
Matplotlib plot of two models' weekly accuracy with error bars, the series shifted slightly left and right so the bars do not overlap
Two series offset by ±0.1 on the x-axis.

Related Matplotlib chart guides:

Frequently asked questions

How do I plot error bars in Matplotlib?

Use plt.errorbar(x, y, yerr=errors, capsize=4). errors can be one number, one value per point, or [lower, upper].

How do I plot error bars without a line?

Pass a marker-only format: plt.errorbar(x, y, yerr=e, fmt="o"). Use fmt="none" to draw only the error bars.

How do I make asymmetric error bars?

Give yerr two sequences: yerr=[lower, upper], where both contain positive distances from each point.

How do I add error bars to a bar chart?

Pass yerr (and capsize) to plt.bar() or ax.bar().

Should error bars show standard deviation or standard error?

Standard deviation shows the spread of the data; standard error (std / √n) shows the uncertainty of the mean. Label which one you use.

How do I change the colour and cap size of error bars?

Use ecolor for the bar colour, elinewidth for their width, and capsize / capthick for the caps.