mrkeyoor.com_
Sun 20 Sept 21:36 UTC
PyPIDataupdated 20 Sept 2026

altair review

Altair 6.2.2 is a Python interface for writing Vega-Lite 6 chart specifications. A chart maps table columns to visual channels such as position, color, size, and tooltip, then a Vega-Lite renderer draws it. That division is useful in notebooks and in systems that store or send chart JSON. Release 6.2.2 fixes duplicate view names in layered concatenations and restores selection parameters inside those nested views. Our Python 3.12 check also found a typed, pure-Python package whose basic import worked.

Verdict

Altair 6.2.2 installed in 0.3 seconds and occupied 12 MB in our sandbox, with a working 1.02-second import and 0 audit findings. Install it when a portable Vega-Lite 6 specification is the deliverable; choose a canvas-first or server-callback tool when the browser grammar becomes the constraint.

We installed it

Lab card: what happened when we installed altairScreenshot of altair documentation
Install✓ · 0.3s11 packages on disk · 12 MB
Importimport altair in 1.02s · pure Python · py.typed · requires Python >=3.10
Known vulns0(pip-audit)

Answers from our run

Does altair install cleanly?

Yes. In a fresh container with an empty cache, pip install altair finished in 0.3s, leaving 11 packages and 12 MB on disk. pip-audit reported no known vulnerabilities.

What does altair need to run?

Python >=3.10, and nothing compiled: it is pure Python. In our run import altair succeeded in 1.02s, and the package ships py.typed for type checkers.

altair or plotly: which should you use?

plotly: Use it when 3D traces or a Dash server callback is part of the product. Altair 6.2.2 installed in 0.3 seconds and occupied 12 MB in our sandbox, with a working 1.02-second import and 0 audit findings.

When should you not use altair?

Your main output is PNG, SVG, or PDF. Those formats need the optional vl-convert-python dependency, while HTML works from the base install.

API stability4/5Altair 6.2.2 keeps the established Chart, mark, encode, transform, parameter, and composition vocabulary, and the current README examples use those same building blocks. The major version still follows Vega-Lite's schema generation: Altair 6 targets Vega-Lite 6 and requires Python 3.10, so a major upgrade can change accepted properties and the renderer contract even when common chart code looks familiar.
Docs5/5The official 6.2.2 site has a task-oriented user guide, an API reference generated from the Vega-Lite schema, a searchable gallery, and separate pages for saving, data transformers, large datasets, renderers, and JupyterChart. Those pages state when `vl-convert-python`, anywidget, or VegaFusion enters the setup. The remaining trap is that a short gallery example can hide the serialization and renderer choices that matter in an application.
Maintenance4/5GitHub showed 10,458 stars, a push on 2026-08-16, and 147 open issues and pull requests in the repository snapshot we fetched. Version 6.2.2 was published on 2026-06-23 with focused fixes for layered concatenation names and selection parameters, plus documentation and dependency upkeep. Work that belongs to Vega-Lite itself can still span two projects, so an Altair issue is not always fixable solely in this repository.
Ecosystem4/5PyPI Stats reported 11,817,567 downloads in the last week, and the documented paths cover Jupyter, VS Code, standalone HTML, JSON, static conversion, and multiple dataframe inputs. Vega-Lite specifications also move cleanly to browser renderers and the online editor. The boundary is equally clear: chart forms outside Vega-Lite usually mean changing libraries, because Altair extensions cannot add an arbitrary low-level drawing model.

Use it if

  • You want charts saved as Vega-Lite JSON or standalone HTML instead of pixels drawn directly by Python.
  • Your notebook needs linked brushing, legend selection, faceting, or input-bound parameters that can run in the browser.
  • Your analysis starts with pandas, Polars, PyArrow, or another supported tabular object and column encodings fit the job.
  • A frontend already renders Vega-Lite 6 and needs chart specifications produced by a Python service.
Skip it if

Setup reality

Our install of Altair 6.2.2 finished in 0.3 seconds in a fresh Python 3.12 container. It left 11 packages and 12 MB on disk. pip-audit reported 0 known vulnerabilities, and import altair completed in 1.02 seconds. The package is pure Python, requires Python 3.10 or newer, ships py.typed, and its metadata listed 47 direct dependencies.

A base install can build a specification and write HTML. Static PNG, SVG, and PDF output goes through vl-convert-python, normally added with the save extra. JupyterChart uses anywidget when a selection or variable must be observed from Python. In a normal script, call save() or show() because leaving a chart as the final expression only displays it in a supported interactive environment.

The default data transformer serializes rows into the chart specification and stops above 5,000 rows with MaxRowsError. That limit applies before the browser performs a Vega-Lite aggregation. Pre-aggregate in Python, point the specification at a URL, or install VegaFusion for supported pre-transforms. Simply disabling the limit can copy a large table into every saved HTML file or notebook output.

Altair 6 emits Vega-Lite 6 specifications, so a separately pinned web renderer must understand that schema version. Parameters and ordinary selections run as Vega expressions in JavaScript. Release 6.2.2 matters for dashboards that combine layering with horizontal or vertical concatenation: it fixes colliding generated view names and makes nested layered views participate in selection parameters.

Patterns

Encode columns in a scatter plot make-scatter-plot

import altair as alt

chart = (
    alt.Chart(cars)
    .mark_point()
    .encode(
        x=alt.X('Horsepower:Q'),
        y=alt.Y('Miles_per_Gallon:Q'),
        color=alt.Color('Origin:N'),
        tooltip=['Name:N', 'Year:T'],
    )
    .properties(width=520, height=300)
)
chart.save('cars.html')

The `Q`, `N`, `O`, and `T` suffixes select quantitative, nominal, ordinal, and temporal field types. A mistaken suffix changes grouping and scale behavior as well as labels.

Aggregate rows in Vega-Lite aggregate-bars

bars = alt.Chart(cars).mark_bar().encode(
    x=alt.X('mean(Miles_per_Gallon):Q', title='Average MPG'),
    y=alt.Y('Origin:N', sort='-x'),
)

`mean(...)` runs after the rows reach Vega-Lite. The source rows still enter the specification and count against the 5,000-row default limit.

Shrink data before chart construction preaggregate-data

summary = (
    sales.groupby('category', as_index=False)
    .agg(amount=('amount', 'sum'))
)

chart = alt.Chart(summary).mark_bar().encode(
    x='category:N',
    y='amount:Q',
)

Pre-aggregation reduces the rows serialized into JSON. Keep the transform in Vega-Lite only when the browser needs access to the original records.

Combine a line with highlighted points layer-charts

base = alt.Chart(stocks).encode(x='date:T', y='price:Q')
line = base.mark_line(color='steelblue')
points = base.mark_circle(size=36, color='navy')

combined = (line + points).resolve_scale(y='shared')

Layered views share scales unless you resolve them differently. Apply top-level configuration after composition because child views cannot own every Vega-Lite config property.

Filter one view with a brush link-brush-filter

brush = alt.selection_interval()

scatter = (
    alt.Chart(cars)
    .mark_point()
    .encode(
        x='Horsepower:Q',
        y='Miles_per_Gallon:Q',
        color=alt.when(brush).then('Origin:N').otherwise(alt.value('lightgray')),
    )
    .add_params(brush)
)

counts = (
    alt.Chart(cars)
    .mark_bar()
    .encode(x='count():Q', y='Origin:N')
    .transform_filter(brush)
)

scatter & counts

`add_params(brush)` must be attached to the view that receives the pointer gesture. The dependent filter has no selection state without it.

Turn a legend into a selector bind-legend

picked = alt.selection_point(fields=['Origin'], bind='legend')

chart = (
    alt.Chart(cars)
    .mark_point()
    .encode(
        x='Horsepower:Q',
        y='Miles_per_Gallon:Q',
        color='Origin:N',
        opacity=alt.when(picked).then(alt.value(1)).otherwise(alt.value(0.1)),
    )
    .add_params(picked)
)

Legend-bound selection executes in the renderer. It still works in standalone HTML because it does not need a Python callback.

Chart a Polars result pass-polars-frame

import altair as alt
import polars as pl

events = pl.read_parquet('events.parquet')
daily = events.group_by('day').len()

chart = alt.Chart(daily).mark_bar().encode(
    x='day:T',
    y='len:Q',
)

Altair accepts the Polars dataframe interchange path, but explicit field types remain useful for categorical values, time zones, and durations.

Write interactive and static output save-chart

chart.save('chart.html')
chart.save('offline.html', inline=True)

# pip install 'altair[save]'
chart.save('chart.png', scale_factor=2)
chart.save('chart.svg')

HTML saving works in the base package. PNG and SVG call the optional static conversion backend, while `inline=True` puts renderer assets into the HTML file.

Register a project theme register-theme

@alt.theme.register('house', enable=True)
def house() -> alt.theme.ThemeConfig:
    return {
        'config': {
            'view': {'continuousWidth': 480, 'continuousHeight': 300},
            'axis': {'grid': False, 'labelFontSize': 12},
        }
    }

Theme dictionaries use Vega-Lite camelCase property names. A misspelled key fails schema validation rather than becoming a Python attribute.

Inspect generated Vega-Lite JSON inspect-spec

spec = chart.to_dict()
json_text = chart.to_json(indent=2)
restored = alt.Chart.from_dict(spec)

# Diagnostic escape hatch only
unvalidated = chart.to_dict(validate=False)

Inline data is present in the returned dictionary. Treat exported specifications as data files when checking privacy, storage, and review diffs.

Observe a brush in Jupyter observe-selection-python

brush = alt.selection_interval(name='brush')
chart = alt.Chart(cars).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
).add_params(brush)

jchart = alt.JupyterChart(chart)

def on_brush(change):
    print(change.new)

jchart.selections.observe(on_brush, ['brush'])
jchart

`JupyterChart` requires anywidget and a running kernel for Python observers. A saved HTML copy keeps the brush but cannot call `on_brush`.

Join data into a TopoJSON map make-choropleth

counties = alt.topo_feature(alt.Data(url=topology_url), 'counties')

chart = (
    alt.Chart(counties)
    .mark_geoshape()
    .encode(color=alt.Color('rate:Q', scale=alt.Scale(scheme='blues')))
    .transform_lookup(
        lookup='id',
        from_=alt.LookupData(rates, 'id', ['rate']),
    )
    .project(type='albersUsa')
)

The second argument to `topo_feature` must match an object stored in the topology. A wrong object name can leave the map empty before a useful Python error appears.

Alternatives

PackageRegistryPick it when
plotlyPyPIUse it when 3D traces or a Dash server callback is part of the product.
matplotlibPyPIUse it for print figures, custom geometry, or control over individual artists.
seabornPyPIUse it for statistical defaults that should produce a matplotlib figure.
bokehPyPIUse it when widgets need a persistent Python server session.

More data guides

numpy · fsspec · pandas · sqlalchemy · pyarrow · lxml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.