|claim|login
RepoCritics — Review. Share. Archive. Every open-source repo.

vega/altair

Wiki: vega/altair

Source: https://github.com/vega/altair

Last synced 2026-07-16 · 1237 words · Edit wiki on GitHub →

vega/altair

> Declarative statistical visualization for Python — a thin, type-checked Python layer that emits Vega-Lite JSON and lets a browser do the drawing.

GitHub repo · Official website · License: BSD-3-Clause

Overview

Vega-Altair (imported as altair) is a declarative charting library for Python, originally written by Jake VanderPlas and Brian Granger in collaboration with the UW Interactive Data Lab1. You describe what the chart should encode — this column to the x-axis, that column to color — and Altair produces a Vega-Lite specification, a JSON grammar of graphics. Altair itself draws nothing; a frontend (JupyterLab, Jupyter Notebook, VS Code, nbviewer, or a static HTML page) hands the spec to the Vega-Lite JavaScript runtime, which compiles it to Vega and renders with D3 in the browser.

The defining design choice is that most of Altair's Python API is auto-generated from the Vega-Lite JSON schema2. This buys unusual internal consistency and full type-checking of chart specs against the grammar, at the cost of an API surface that tracks Vega-Lite's vocabulary rather than Python idioms. If you understand Vega-Lite's model — marks, encodings, transforms, selections/parameters — Altair feels obvious; if you don't, the errors can be opaque because they surface at the schema level.

As of 2026 the project is broadly maintained (roughly 10.4k stars, 859 forks, recent commits) and is the mainstream declarative option in the PyData stack, sitting alongside matplotlib, plotly, and bokeh. Its niche is exploratory statistical graphics and interactive charts (linked brushing, filtering) written in a handful of lines. It is not a general-purpose rendering engine, and its browser-first, data-embedded model is the source of most of its footguns.

Getting Started

pip install altair            # core library
pip install "altair[all]"     # + vl-convert, vegafusion for export/large data
# conda install altair -c conda-forge
import altair as alt
from vega_datasets import data

cars = data.cars()

chart = (
    alt.Chart(cars)
    .mark_point()
    .encode(
        x="Horsepower",
        y="Miles_per_Gallon",
        color="Origin",
    )
)
chart.save("cars.png")   # requires vl-convert

Linked interaction — a scatter selection filtering a bar chart — is the feature that distinguishes Altair from static libraries:

brush = alt.selection_interval()

points = (
    alt.Chart(cars).mark_point().encode(
        x="Horsepower", y="Miles_per_Gallon",
        color=alt.when(brush).then("Origin").otherwise(alt.value("lightgray")),
    ).add_params(brush)
)
bars = (
    alt.Chart(cars).mark_bar().encode(
        y="Origin", x="count()", color="Origin"
    ).transform_filter(brush)
)

points & bars   # vertical concatenation

Architecture / How It Works

The pipeline is: Python objects → Vega-Lite JSON → (Vega-Lite JS compiler) → Vega → rendered SVG/Canvas. Altair owns only the first arrow.

  • Schema-generated API. The bulk of altair.vegalite is generated by tooling in the repo (tools/) from the Vega-Lite schema. Chart, mark_*, encode, channel classes, and transform methods mirror the JSON grammar. This is why chart definitions round-trip cleanly to and from .to_dict() / .to_json(), and why validation is a schema check, not hand-written Python.
  • Data is embedded by default. When you pass a pandas DataFrame, Altair serializes the whole dataset inline into the spec's data.values. This makes charts self-contained (a single JSON blob renders anywhere) but couples spec size to row count. To avoid it, use a data transformer that writes to URLs/JSON files, or push the data through a server-side engine (see below).
  • Parameters and selections. Interactivity (selection_interval, selection_point, bindings, alt.param) compiles to Vega-Lite params. The interaction logic runs entirely in the browser via the Vega runtime — there is no Python callback loop, which is why Altair charts work in static HTML and notebooks-on-GitHub but cannot run arbitrary Python on click.
  • Rendering is someone else's job. Altair uses a renderer/mime-bundle mechanism: in a notebook it emits a Vega-Lite mime type that the frontend's JS renders. alt.renderers.enable(...) selects behavior. There is no built-in image rasterizer.
  • Static export via vl-convert. Turning a spec into PNG/SVG/PDF without a browser is handled by vl-convert (Rust, bundles Deno + the Vega JS libs). This replaced the older altair_saver + Selenium/headless-Chrome path, which was the historical pain point for CI and servers.
  • Server-side transforms via VegaFusion. VegaFusion can execute Vega-Lite transforms and data pruning in Rust before the spec reaches the browser, enabled with alt.data_transformers.enable("vegafusion"). This is the sanctioned route to charts over large datasets.

Production Notes

  • The 5,000-row limit is the first thing you will hit. By default Altair raises MaxRowsError when a DataFrame exceeds ~5,000 rows, precisely because the data is being embedded in the spec. Raising the cap (alt.data_transformers.disable_max_rows()) works but produces multi-megabyte specs that lag or crash the browser. The correct fixes are aggregating before plotting, switching to a URL/JSON data transformer, or enabling VegaFusion — not disabling the guard.
  • Everything renders client-side. Chart performance is bounded by the viewer's browser and the Vega-Lite JS runtime, not by Python. Tens of thousands of marks will stutter regardless of how fast your Python is. Altair is for summaries and samples, not million-point scatterplots.
  • Export has real dependencies. chart.save("x.png"/"x.svg"/"x.pdf") needs vl-convert-python installed; older tutorials referencing altair_saver, Selenium, or a Chrome driver are outdated and should not be reproduced in new environments.
  • Version pinning matters across the JS boundary. A given Altair release targets a specific Vega-Lite (and thus Vega) version. Offline deployments, custom renderers, or embedding require matching the Python-side and JS-side versions; mismatches produce charts that validate in Python but render blank or wrong.
  • Notebook display quirks. A chart may show in JupyterLab but not in a plain script, or not in an environment without the JS assets/internet for the Vega CDN. For self-contained HTML use chart.save("x.html"); for fully offline embedding, ensure the Vega libraries are bundled rather than CDN-loaded.
  • Opaque schema errors. Because validation is against the Vega-Lite JSON schema, a typo in a channel or an unsupported encoding often yields a SchemaValidationError pointing at JSON paths, not a Pythonic message. Reading the Vega-Lite docs is frequently faster than reading the traceback.

When to Use / When Not

Use when:

  • You want concise, declarative statistical charts and value a consistent, type-checked API.
  • You need lightweight interactivity — linked brushing, cross-filtering, tooltips — without writing a JS app.
  • You work in notebooks and want charts that survive as JSON and render on GitHub/nbviewer.
  • Your data fits in memory and, after aggregation, in the thousands-of-rows range.

Avoid when:

  • You need to render millions of points, or want GPU/WebGL-scale plotting.
  • You need imperative, pixel-level control over a static publication figure (matplotlib fits better).
  • You need Python-side callbacks on chart interaction, or a full dashboard server with app state.
  • You want zero extra dependencies for image export in a headless environment and can't add vl-convert.

Alternatives

  • matplotlib/matplotlib — imperative, pixel-precise, static-first; reach for it for publication figures and full control.
  • plotly/plotly.py — declarative interactive charts with a broader chart set (3D, maps) and the Dash dashboard ecosystem; use when you need richer interactivity or an app framework.
  • bokeh/bokeh — Python-native interactive plotting with a server for streaming/large data and custom widgets; use when interaction must be driven from Python.
  • mwaskom/seaborn — high-level statistical plotting on matplotlib; use when you want quick static statistical graphics without the browser.
  • holoviz/hvplot — one high-level .hvplot API over bokeh/matplotlib/plotly with big-data support via Datashader; use when you want to switch backends or handle very large datasets.

History

VersionDateNotes
1.02016First release, targeting Vega-Lite 1.x1.
2.02018Rebuilt on Vega-Lite 2; API broadly reshaped3.
3.02019Vega-Lite 3; expanded selection/transform coverage3.
4.02019Vega-Lite 4; renderer and theming changes3.
5.02023Vega-Lite 5; parameters model, vl-convert for export, VegaFusion integration3.

References

  1. ^ Jacob VanderPlas et al., "Altair: Interactive Statistical Visualizations for Python," Journal of Open Source Software 3(32):1057, 2018. https://joss.theoj.org/papers/10.21105/joss.01057
  2. ^ Project README and docs, "Auto-generated internal Python API that guarantees visualizations are ... in full conformance with the Vega-Lite specification." https://altair-viz.github.io/
  3. ^ Vega-Altair release history / changelog. https://github.com/vega/altair/releases

Tags

python, data-visualization, declarative, vega-lite, statistical-graphics, charting, interactive-visualization, notebook, json-grammar, dataframe