Error bars show uncertainty around plotted measurements instead of leaving each marker as a single exact value. In Matplotlib, Axes.errorbar() draws the marker or line and the horizontal or vertical uncertainty range on the same axes, so benchmark runs, lab readings, and summary statistics can carry visible variation.

The xerr and yerr arguments control where the uncertainty is drawn. A scalar applies the same symmetric value to every point, a one-dimensional sequence gives each point its own symmetric range, and a two-row sequence stores separate lower and upper values for asymmetric ranges.

A small benchmark plot makes the input shapes visible in one exported PNG. The left panel uses one yerr value per point, while the right panel combines horizontal xerr values with a two-row asymmetric yerr array.

Steps to add Matplotlib error bars:

  1. Save the error bar script as error_bar_add.py.
    error_bar_add.py
    from pathlib import Path
     
    import matplotlib
    import matplotlib.pyplot as plt
    import numpy as np
     
     
    output = Path("error-bar-add.png")
     
    days = np.array([1, 2, 3, 4, 5])
    mean_latency = np.array([42, 39, 36, 38, 35])
    latency_error = np.array([2.5, 2.0, 1.8, 2.2, 1.6])
     
    batch_size = np.array([20, 40, 60, 80, 100])
    throughput = np.array([120, 168, 205, 236, 251])
    batch_error = np.array([3, 4, 5, 5, 6])
    lower_error = np.array([9, 12, 15, 13, 14])
    upper_error = np.array([14, 16, 20, 18, 21])
    asymmetric_error = np.vstack([lower_error, upper_error])
     
    fig, (ax_left, ax_right) = plt.subplots(
        ncols=2,
        figsize=(8.2, 3.6),
        layout="constrained",
    )
     
    symmetric = ax_left.errorbar(
        days,
        mean_latency,
        yerr=latency_error,
        fmt="o-",
        capsize=4,
        elinewidth=1.4,
        ecolor="0.25",
        label="Mean latency",
    )
    ax_left.set_title("Symmetric y error")
    ax_left.set_xlabel("Test day")
    ax_left.set_ylabel("Latency (ms)")
    ax_left.grid(True, axis="y", alpha=0.25)
    ax_left.legend()
     
    asymmetric = ax_right.errorbar(
        batch_size,
        throughput,
        xerr=batch_error,
        yerr=asymmetric_error,
        fmt="s-",
        capsize=4,
        elinewidth=1.4,
        ecolor="0.25",
        color="tab:green",
        label="Throughput",
    )
    ax_right.set_title("Asymmetric y and x error")
    ax_right.set_xlabel("Batch size")
    ax_right.set_ylabel("Rows per second")
    ax_right.grid(True, axis="y", alpha=0.25)
    ax_right.legend()
     
    fig.savefig(output, dpi=160)
    plt.close(fig)
     
    symmetric_caplines = symmetric.lines[1]
    asymmetric_caplines = asymmetric.lines[1]
     
    print(f"matplotlib {matplotlib.__version__}")
    print(f"symmetric points: {len(days)}")
    print(f"symmetric cap artists: {len(symmetric_caplines)}")
    print(f"asymmetric shape: {asymmetric_error.shape}")
    print(f"x error points: {len(batch_error)}")
    print(f"asymmetric cap artists: {len(asymmetric_caplines)}")
    print(f"saved: {output}")
    print(f"bytes: {output.stat().st_size}")

    capsize draws end caps on the uncertainty bars, elinewidth controls the bar stroke width, and ecolor sets the bar color independently from the plotted line.

  2. Run the script from the Python environment that has Matplotlib installed.
    $ python error_bar_add.py
    matplotlib 3.11.0
    symmetric points: 5
    symmetric cap artists: 2
    asymmetric shape: (2, 5)
    x error points: 5
    asymmetric cap artists: 4
    saved: error-bar-add.png
    bytes: 76654

    The byte count can change with Matplotlib, font, backend, or DPI differences. A nonzero byte count and the expected filename confirm that the PNG was written.

  3. Open error-bar-add.png and confirm the uncertainty bars.

    The left panel should show vertical symmetric bars around every marker. The right panel should show horizontal xerr bars plus asymmetric vertical bars where the lower and upper distances differ.

  4. Use a two-row array when lower and upper uncertainty values differ.
    x_error = np.array([3, 4, 5, 5, 6])
    lower_error = np.array([9, 12, 15, 13, 14])
    upper_error = np.array([14, 16, 20, 18, 21])
    yerr = np.vstack([lower_error, upper_error])
     
    ax.errorbar(batch_size, throughput, xerr=x_error, yerr=yerr, fmt="o", capsize=4)

    Matplotlib expects asymmetric error arrays in shape (2, N), where row 0 is the lower distance and row 1 is the upper distance. Error values are distances from the plotted point, not absolute bounds, and must be nonnegative.

  5. Remove the temporary files when they were created only for testing.
    $ rm error_bar_add.py error-bar-add.png