Altair Interactivity
Altair’s biggest differentiator: charts are interactive by default in a browser/Jupyter, and that interactivity is genuinely built into the spec, not bolted on.
🖱️ Basic built-in interactivity
alt.Chart(df).mark_point().encode(x="a", y="b").interactive() # enables pan + zoom, one call💬 Tooltips
alt.Chart(df).mark_point().encode(
x="a", y="b",
tooltip=["a", "b", "category"]
)
alt.Chart(df).mark_point().encode(
tooltip=[alt.Tooltip("value:Q", format=".2f", title="Value")] # formatted tooltip
)🎯 Selections (params) — the core interactivity mechanism
Modern Altair (5.x) uses alt.selection_point() / alt.selection_interval() + .add_params().
brush = alt.selection_interval() # drag-select a rectangular region
alt.Chart(df).mark_point().encode(
x="a", y="b",
color=alt.condition(brush, "category:N", alt.value("lightgray")) # highlight selected points
).add_params(brush)click = alt.selection_point(fields=["category"]) # click to select a category
alt.Chart(df).mark_bar().encode(
x="category:N", y="value:Q",
opacity=alt.condition(click, alt.value(1), alt.value(0.3))
).add_params(click)| Selection type | Interaction |
|---|---|
selection_point() | click a mark to select it |
selection_point(fields=["col"]) | click selects every mark sharing that field’s value |
selection_interval() | drag a rectangular region (brush) |
selection_interval(encodings=["x"]) | brush limited to one axis |
🎨 alt.condition — the “if selected, then” pattern
color=alt.condition(selection, "category:N", alt.value("lightgray"))Reads as: if a mark is part of selection, color it by category; otherwise, gray it out.
alt.condition(predicate, if_true, if_false)is the single most important interactivity pattern in Altair — nearly every "highlight on click/hover" chart uses this exact shape.
🔗 Cross-filtering between two charts
brush = alt.selection_interval()
points = alt.Chart(df).mark_point().encode(
x="a:Q", y="b:Q",
color=alt.condition(brush, "category:N", alt.value("lightgray"))
).add_params(brush)
bars = alt.Chart(df).mark_bar().encode(
x="category:N",
y="count():Q"
).transform_filter(brush) # bars update to reflect only the brushed selection
points | bars # side-by-side, linkedLinking works because
brushis a shared parameter —add_params()on one chart,transform_filter()on the other, referencing the same object.
🖱️ Hover-based selection
hover = alt.selection_point(on="mouseover", fields=["category"], nearest=True)
alt.Chart(df).mark_point().encode(
x="a", y="b",
size=alt.condition(hover, alt.value(200), alt.value(50))
).add_params(hover)🎚️ Interactive parameter (e.g. a slider)
slider = alt.binding_range(min=0, max=100, step=1, name="Threshold ")
param = alt.param(bind=slider, value=50)
alt.Chart(df).mark_point().add_params(param).transform_filter(
alt.datum.value > param
).encode(x="a", y="b")