Introduction to Altair: A Declarative Visualization Library in Python
Data visualization is an essential tool for understanding and communicating patterns in data. While there are many great open source Python libraries for creating statistical graphics, like Matplotlib, Seaborn, and Plotly, a relative newcomer called Altair has been gaining popularity in the Python data science community.
Altair brings a declarative, "grammar of graphics" approach to visualization that can dramatically simplify the creation of rich statistical charts. In this article, we‘ll take a deep dive into what sets Altair apart and walk through several examples of how it can help you up your visualization game as a data scientist.
The Power of Declarative Visualization
The key idea behind Altair is that it uses a declarative rather than imperative approach to visualization. Imperative visualization code, like what you write with Matplotlib, consists of step-by-step instructions that explicitly set up the plot components:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.scatter(x, y, c=color, s=size)
ax.set_xlabel(‘X Label‘)
ax.set_ylabel(‘Y Label‘)
ax.set_title(‘Title‘)
fig.tight_layout()
While this approach is flexible, it requires a lot of code to create even simple charts and obscures the actual mapping of data to visual properties.
In contrast, declarative visualization code simply declares the relationship between data variables and visual properties like position, color, size, etc. The details of rendering are handled automatically by the library.
Here‘s the equivalent scatter plot in Altair:
import altair as alt
chart = alt.Chart(df).mark_point().encode(
x=‘x‘,
y=‘y‘,
color=‘color‘,
size=‘size‘
)
This specifies that the x column should be represented by the x-axis position, the y column by the y-axis position, the color column by the color of the points, and the size column by the size of the points.
The declarative approach has several benefits:
- Extremely concise code, typically 5-10x shorter than the imperative equivalent
- Easier to write and understand at a glance what the chart is representing
- Less focus on plot implementation details, more on the data to visual property mappings
- Enforces visualization best practices and a structured workflow with sensible defaults
The declarative model of visualization was pioneered by Leland Wilkinson in his landmark 1999 book The Grammar of Graphics. The idea is that all statistical graphics can be represented by a consistent "grammar" composed of:
- Data: The dataset consisting of variables (columns) and observations (rows)
- Transformations: Filters, aggregations, window functions applied to the data
- Mark: The geometric object used to represent the data, e.g. point, line, bar
- Encoding: The mapping of variables to visual properties of the mark, e.g. position, size, color
- Scale: The transformation from data values to visual values, e.g. linear, log, categorical color palettes
- Guide: Axes, legends, and other annotations used to interpret the chart
This grammar provides a structured mental model for reasoning about visualizations and enables very concise, expressive specification of a wide range of charts.
The first widely used implementation of this model was the ggplot2 library for R created by Hadley Wickham. However, similar declarative libraries have since been created for Python (Altair, Plotnine), JavaScript (Vega-Lite, D3), and Julia (VegaLite.jl).
Exploring Data with Altair
To see how Altair‘s declarative API can streamline data exploration, let‘s dig into some examples using the classic penguins dataset. This dataset, collected by Dr. Kristen Gorman and the Palmer Station Long Term Ecological Research program, contains measurements of bill length, bill depth, flipper length, body mass, and other attributes for 344 penguins of 3 different species.
First, let‘s load the data into a Pandas DataFrame:
import pandas as pd
penguins = pd.read_csv(‘penguins.csv‘)
penguins.head()
Next, we‘ll create a scatter plot comparing the bill length and depth for each species:
import altair as alt
chart = alt.Chart(penguins).mark_point().encode(
x=‘bill_length_mm‘,
y=‘bill_depth_mm‘,
color=‘species‘
)
chart
This produces an interactive Vega-Lite chart with bill length on the x-axis, bill depth on the y-axis, and the points colored by the species. We can pan and zoom, and hovering over a point displays a tooltip with its values.
Already we can see a clear separation between the species, with Adelie penguins having the smallest bills, Gentoo penguins the largest, and Chinstrap penguins in between.
Next, let‘s look at the distribution of flipper lengths with a histogram:
chart = alt.Chart(penguins).mark_bar().encode(
x=alt.X(‘flipper_length_mm‘, bin=alt.Bin(maxbins=20)),
y=‘count()‘
)
chart
Here we‘ve specified mark_bar() to use a bar mark and passed a binning transformation to the x encoding to group the flipper length values into 20 bins. The y encoding is set to count() to compute the number of observations in each bin.
The histogram shows that the flipper lengths appear to be normally distributed, with most penguins having flippers between 180-210mm long. However, the distribution seems to be bimodal, with peaks around 190mm and 210mm. Perhaps this is due to the different species?
We can investigate that hypothesis by adding a color encoding to separate the species:
chart = alt.Chart(penguins).mark_bar().encode(
x=alt.X(‘flipper_length_mm‘, bin=alt.Bin(maxbins=20)),
y=‘count()‘,
color=‘species‘
)
chart
The stacked histogram reveals that the bimodality is indeed due to the different species, with Adelie penguins making up the smaller flipper peak and Gentoo penguins the larger peak.
Finally, let‘s visualize the relationship between bill length, bill depth, and species using a 2D histogram heatmap:
chart = alt.Chart(penguins).mark_rect().encode(
x=alt.X(‘bill_length_mm‘, bin=alt.Bin(maxbins=20)),
y=alt.Y(‘bill_depth_mm‘, bin=alt.Bin(maxbins=20)),
color=alt.Color(‘count()‘, scale=alt.Scale(scheme=‘greenblue‘)),
facet=‘species‘
)
chart
This uses a rectangular mark_rect() to represent the count of observations within each 2D bin of bill length and depth. The facet encoding splits the chart into subplots for each species.
The heatmaps show that for Adelie and Chinstrap penguins, there is a positive correlation between bill length and depth (i.e. penguins with longer bills also tend to have deeper bills). In contrast, Gentoo penguins have a more circular distribution with less correlation between bill dimensions.
Hopefully these examples demonstrate how Altair‘s declarative API can allow you to rapidly explore a high-dimensional dataset by easily mapping variables to visual encodings. The consistent "grammar of graphics" structure makes it intuitive to modify charts by adding or changing encodings.
Advantages of the Grammar of Graphics
The grammar of graphics and declarative API provided by Altair encourage visualization best practices that produce more effective, reproducible charts. Some key advantages include:
- Separating data manipulation (filtering, aggregation, etc.) from mapping to visual properties, facilitating an iterative, layered chart-building approach
- Enforcing sensible defaults aligned with perceptual principles and visualization research, requiring less fine-tuning to produce good charts
- Consistency across plots – once you learn the grammar, you can quickly translate analysis questions to the appropriate chart and encodings
- Easier to create multi-view dashboards and "small multiples" by reusing chart code and adding faceting encodings
- Avoiding common pitfalls like overplotting, ineffective visual encodings, inconsistent axis/legend properties, excessive ink vs. data ratio
Of course, the grammar isn‘t suitable for every visualization need. Sometimes you need more low-level control over plot details or to create highly customized graphics. In those cases, imperative libraries like Matplotlib are often a better choice.
But for the vast majority of statistical visualization and exploratory data analysis, the structured workflow and constraints provided by the grammar of graphics will make you a more efficient and effective data communicator.
Altair Ecosystem and Resources
Altair is an open source project under active development by Jake Vanderplas, Brian Granger, and other contributors. The library is maintained by the Urban Institute and is fiscally sponsored by NumFOCUS, a non-profit that supports open source scientific computing projects.
Since its initial release in 2016, Altair has seen strong adoption and growth within the Python data science community. As of July 2022, the package has been downloaded over 10 million times from the Python Package Index and has over 7,500 GitHub stars.
Altair has also been a popular topic at PyCon, SciPy, and PyData conferences, with standing-room only crowds at talks and tutorials. In the 2021 Stack Overflow Developer Survey, Altair was the 5th most commonly used Python data visualization library behind Matplotlib, Plotly, Seaborn, and Bokeh.
Some key resources for learning more about Altair and the grammar of graphics approach include:
- Altair Documentation: Comprehensive user guide and API reference with hundreds of examples
- Altair Example Gallery: Filterable gallery of charts demonstrating various encodings and techniques
- Altair Tutorial: In-depth tutorial notebooks explaining key concepts and walking through real-world examples
- Declarative Visualization with Altair (PyCon 2018 Talk): Introduction to the grammar of graphics and interactive Altair demos by Jake Vanderplas
- Data Visualization with Altair: Full-length tutorial introducing Altair and visualization principles from the University of Washington
- The Grammar of Graphics (textbook): Original book by Leland Wilkinson introducing the grammar of graphics framework
Conclusion
In this article, we‘ve taken a deep dive into Altair, a declarative statistical visualization library for Python. We‘ve explored how its "grammar of graphics" approach enables concise, expressive specification of a wide range of charts by simply mapping data variables to visual encoding channels.
Through examples, we‘ve seen how Altair‘s API can streamline exploratory data analysis by allowing you to rapidly ask and answer questions about relationships between variables. We‘ve also discussed how the declarative API and enforcement of sensible defaults encourages visualization best practices.
While Altair isn‘t the right tool for every job, its focus on concisely representing the links between data and visual properties makes it an enormously productive tool for data scientists. Used alongside Pandas for data manipulation and scikit-learn for modeling, Altair completes a powerful PyData trifecta for exploring and communicating insights from data.
The grammar of graphics isn‘t a magic bullet that automatically produces amazing visualizations. It still requires you to think carefully about your message and design choices. But by giving you a structured framework for reasoning about charts and eliminating a lot of implementation distractions, the grammar can help you elevate your visualizations and be a more effective data storyteller.
So give Altair a shot in your next data science project, and see how it can change the way you see and show your data!