Altair
Data Visualization
Line Chart
Confidence Interval
Python

Line Chart with Custom Confidence Interval in Altair

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

If you already have your own lower and upper confidence bounds, the right Altair pattern is to draw the interval as an area band and layer a line on top of it. That is different from Altair's automatic error-band calculations, which compute intervals from raw data. For model outputs, forecasts, or pre-aggregated statistics, custom columns are usually the correct input.

Structure The Data Explicitly

For a custom confidence interval, your table should usually contain at least these columns:

  • an x-axis field such as date or step
  • a center line such as mean
  • a lower bound
  • an upper bound

Example data:

python
1import pandas as pd
2
3source = pd.DataFrame(
4    {
5        "day": pd.date_range("2026-01-01", periods=5, freq="D"),
6        "mean": [10, 12, 11, 15, 14],
7        "lower": [8, 10, 9, 13, 12],
8        "upper": [12, 14, 13, 17, 16],
9    }
10)

Once the bounds are explicit, Altair does not need to infer anything statistically. It just visualizes the band you provide.

Build The Confidence Band With mark_area

The interval band is typically an area with y for the lower bound and y2 for the upper bound.

python
1import altair as alt
2
3band = alt.Chart(source).mark_area(opacity=0.25, color="#7aa6c2").encode(
4    x="day:T",
5    y="lower:Q",
6    y2="upper:Q",
7)

This creates the shaded region. The important detail is y2, which tells Altair where the area ends vertically.

Layer The Center Line On Top

Now add the main line and combine the two charts.

python
1line = alt.Chart(source).mark_line(color="#1f4e79", strokeWidth=2).encode(
2    x="day:T",
3    y="mean:Q",
4    tooltip=["day:T", "mean:Q", "lower:Q", "upper:Q"],
5)
6
7chart = (band + line).properties(width=600, height=300)
8chart

This is the standard pattern for a custom confidence interval in Altair:

  • one layer for uncertainty
  • one layer for the estimate

Because they share the same x-axis, the result reads naturally.

Full Runnable Example

python
1import pandas as pd
2import altair as alt
3
4source = pd.DataFrame(
5    {
6        "day": pd.date_range("2026-01-01", periods=5, freq="D"),
7        "mean": [10, 12, 11, 15, 14],
8        "lower": [8, 10, 9, 13, 12],
9        "upper": [12, 14, 13, 17, 16],
10    }
11)
12
13band = alt.Chart(source).mark_area(opacity=0.25, color="#7aa6c2").encode(
14    x=alt.X("day:T", title="Day"),
15    y=alt.Y("lower:Q", title="Value"),
16    y2="upper:Q",
17)
18
19line = alt.Chart(source).mark_line(color="#1f4e79", strokeWidth=2).encode(
20    x="day:T",
21    y="mean:Q",
22    tooltip=["day:T", "mean:Q", "lower:Q", "upper:Q"],
23)
24
25chart = (band + line).properties(
26    width=600,
27    height=300,
28    title="Forecast With Custom Confidence Interval",
29)
30
31chart.show()

If you are in a Jupyter notebook, just evaluate chart instead of calling show().

When To Use mark_errorband Instead

Altair also supports mark_errorband, but that is a better fit when Altair should compute the interval from raw observations. If you already have a lower and upper column from a model, bootstrap procedure, or external computation, mark_area plus mark_line is more explicit and easier to control.

That distinction prevents a lot of confusion:

  • computed interval from raw rows: consider mark_errorband
  • custom interval columns already present: use mark_area with y and y2

Improve Readability

A few practical improvements help a lot:

  • keep the band semi-transparent so the line stays visible
  • choose a darker line color than the fill color
  • add tooltips for the center value and both bounds
  • sort the data by the x-axis field before plotting

If the x-axis is a category rather than time, the same layering pattern still works.

Common Pitfalls

  • Using mark_errorband when the interval is already precomputed in the data.
  • Forgetting y2, which means Altair has no upper boundary for the band.
  • Plotting unsorted x values, which can make the line and band zig-zag unexpectedly.
  • Making the band fully opaque so it hides the center line.
  • Mixing aggregated and row-level data in the same chart without understanding which fields are already summarized.

Summary

  • For custom confidence intervals in Altair, use a layered chart.
  • Draw the interval with mark_area using y and y2.
  • Draw the center trend with mark_line on top.
  • Use mark_errorband only when Altair should compute the interval from raw data.
  • Keep the data explicit with separate columns for center, lower, and upper values.

Course illustration
Course illustration

All Rights Reserved.