Simple Charts: Core Concepts¶
The goal of this section is to teach you the core concepts required to create a basic Altair chart; namely:
Data, Marks, and Encodings: the three core pieces of an Altair chart
Encoding Types:
Q
(quantitative),N
(nominal),O
(ordinal),T
(temporal), which drive the visual representation of the encodingsBinning and Aggregation: which let you control aspects of the data representation within Altair.
With a good understanding of these core pieces, you will be well on your way to making a variety of charts in Altair.
We’ll start by importing Altair, and (if necessary) enabling the appropriate renderer:
import altair as alt
A Basic Altair Chart¶
The essential elements of an Altair chart are the data, the mark, and the encoding.
The format by which these are specified will look something like this:
alt.Chart(data).mark_point().encode(
encoding_1='column_1',
encoding_2='column_2',
# etc.
)
Let’s take a look at these pieces, one at a time.
The Data¶
Data in Altair is built around the Pandas Dataframe. For this section, we’ll use the cars dataset that we saw before, which we can load using the vega_datasets package:
from vega_datasets import data
cars = data.cars()
cars.head()
Name | Miles_per_Gallon | Cylinders | Displacement | Horsepower | Weight_in_lbs | Acceleration | Year | Origin | |
---|---|---|---|---|---|---|---|---|---|
0 | chevrolet chevelle malibu | 18.0 | 8 | 307.0 | 130.0 | 3504 | 12.0 | 1970-01-01 | USA |
1 | buick skylark 320 | 15.0 | 8 | 350.0 | 165.0 | 3693 | 11.5 | 1970-01-01 | USA |
2 | plymouth satellite | 18.0 | 8 | 318.0 | 150.0 | 3436 | 11.0 | 1970-01-01 | USA |
3 | amc rebel sst | 16.0 | 8 | 304.0 | 150.0 | 3433 | 12.0 | 1970-01-01 | USA |
4 | ford torino | 17.0 | 8 | 302.0 | 140.0 | 3449 | 10.5 | 1970-01-01 | USA |
Data in Altair is expected to be in a tidy format; in other words:
each row is an observation
each column is a variable
See Altair’s Data Documentation for more information.
The Chart object¶
With the data defined, you can instantiate Altair’s fundamental object, the Chart
. Fundamentally, a Chart
is an object which knows how to emit a JSON dictionary representing the data and visualization encodings, which can be sent to the notebook and rendered by the Vega-Lite JavaScript library.
Let’s take a look at what this JSON representation looks like, using only the first row of the data:
cars1 = cars.iloc[:1]
alt.Chart(cars1).mark_point().to_dict()
{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},
'data': {'name': 'data-36a712fbaefa4d20aa0b32e160cfd83a'},
'mark': 'point',
'$schema': 'https://vega.github.io/schema/vega-lite/v4.8.1.json',
'datasets': {'data-36a712fbaefa4d20aa0b32e160cfd83a': [{'Name': 'chevrolet chevelle malibu',
'Miles_per_Gallon': 18.0,
'Cylinders': 8,
'Displacement': 307.0,
'Horsepower': 130.0,
'Weight_in_lbs': 3504,
'Acceleration': 12.0,
'Year': '1970-01-01T00:00:00',
'Origin': 'USA'}]}}
At this point the chart includes a JSON-formatted representation of the dataframe, what type of mark to use, along with some metadata that is included in every chart output.
The Mark¶
We can decide what sort of mark we would like to use to represent our data.
In the previous example, we can choose the point
mark to represent each data as a point on the plot:
alt.Chart(cars).mark_point()
The result is a visualization with one point per row in the data, though it is not a particularly interesting: all the points are stacked right on top of each other!
It is useful to again examine the JSON output here:
alt.Chart(cars1).mark_point().to_dict()
{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},
'data': {'name': 'data-36a712fbaefa4d20aa0b32e160cfd83a'},
'mark': 'point',
'$schema': 'https://vega.github.io/schema/vega-lite/v4.8.1.json',
'datasets': {'data-36a712fbaefa4d20aa0b32e160cfd83a': [{'Name': 'chevrolet chevelle malibu',
'Miles_per_Gallon': 18.0,
'Cylinders': 8,
'Displacement': 307.0,
'Horsepower': 130.0,
'Weight_in_lbs': 3504,
'Acceleration': 12.0,
'Year': '1970-01-01T00:00:00',
'Origin': 'USA'}]}}
Notice that now in addition to the data, the specification includes information about the mark type.
There are a number of available marks that you can use; some of the more common are the following:
mark_point()
mark_circle()
mark_square()
mark_line()
mark_area()
mark_bar()
mark_tick()
You can get a complete list of mark_*
methods using Jupyter’s tab-completion feature: in any cell just type:
alt.Chart.mark_
followed by the tab key to see the available options.
Encodings¶
The next step is to add visual encoding channels (or encodings for short) to the chart. An encoding channel specifies how a given data column should be mapped onto the visual properties of the visualization. Some of the more frequenty used visual encodings are listed here:
x
: x-axis valuey
: y-axis valuecolor
: color of the markopacity
: transparency/opacity of the markshape
: shape of the marksize
: size of the markrow
: row within a grid of facet plotscolumn
: column within a grid of facet plots
For a complete list of these encodings, see the Encodings section of the documentation.
Visual encodings can be created with the encode()
method of the Chart
object. For example, we can start by mapping the y
axis of the chart to the Origin
column:
alt.Chart(cars).mark_point().encode(
y='Origin'
)
The result is a one-dimensional visualization representing the values taken on by Origin
, with the points in each category on top of each other.
As above, we can view the JSON data generated for this visualization:
alt.Chart(cars1).mark_point().encode(
x='Origin'
).to_dict()
{'config': {'view': {'continuousWidth': 400, 'continuousHeight': 300}},
'data': {'name': 'data-36a712fbaefa4d20aa0b32e160cfd83a'},
'mark': 'point',
'encoding': {'x': {'type': 'nominal', 'field': 'Origin'}},
'$schema': 'https://vega.github.io/schema/vega-lite/v4.8.1.json',
'datasets': {'data-36a712fbaefa4d20aa0b32e160cfd83a': [{'Name': 'chevrolet chevelle malibu',
'Miles_per_Gallon': 18.0,
'Cylinders': 8,
'Displacement': 307.0,
'Horsepower': 130.0,
'Weight_in_lbs': 3504,
'Acceleration': 12.0,
'Year': '1970-01-01T00:00:00',
'Origin': 'USA'}]}}
The result is the same as above with the addition of the 'encoding'
key, which specifies the visualization channel (y
), the name of the field (Origin
), and the type of the variable (nominal
).
We’ll discuss these data types in a moment.
The visualization can be made more interesting by adding another channel to the encoding: let’s encode the Miles_per_Gallon
as the x
position:
alt.Chart(cars).mark_point().encode(
y='Origin',
x='Miles_per_Gallon'
)
You can add as many encodings as you wish, with each encoding mapped to a column in the data. For example, here we will color the points by Origin, and plot Miles_per_gallon vs Year:
alt.Chart(cars).mark_point().encode(
color='Origin',
y='Miles_per_Gallon',
x='Year'
)
Excercise: Exploring Data¶
Now that you know the basics (Data, encodings, marks) take some time and try making a few plots!
In particular, I’d suggest trying various combinations of the following:
Marks:
mark_point()
,mark_line()
,mark_bar()
,mark_text()
,mark_rect()
…Data Columns:
'Acceleration'
,'Cylinders'
,'Displacement'
,'Horsepower'
,'Miles_per_Gallon'
,'Name'
,'Origin'
,'Weight_in_lbs'
,'Year'
Encodings:
x
,y
,color
,shape
,row
,column
,opacity
,text
,tooltip
…
Work with a partner to use various combinations of these options, and see what you can learn from the data! In particular, think about the following:
Which encodings go well with continuous, quantitative values?
Which encodings go well with discrete, categorical (i.e. nominal) values?
After about 10 minutes, we’ll ask for a couple volunteers to share their combination of marks, columns, and encodings.
Encoding Types¶
One of the central ideas of Altair is that the library will choose good defaults for your data type.
The basic data types supported by Altair are as follows:
Data Type | Code | Description |
---|---|---|
quantitative | Q | Numerical quantity (real-valued) |
nominal | N | Name / Unordered categorical |
ordinal | O | Ordered categorial |
temporal | T | Date/time |
When you specify data as a pandas dataframe, these types are automatically determined by Altair.
When you specify data as a URL, you must manually specify data types for each of your columns.
Let’s look at a simple plot containing three of the columns from the cars data:
alt.Chart(cars).mark_tick().encode(
x='Miles_per_Gallon',
y='Origin',
color='Cylinders'
)
Questions:
what data type best goes with
Miles_per_Gallon
?what data type best goes with
Origin
?what data type best goes with
Cylinders
?
Let’s add the shorthands for each of these data types to our specification, using the one-letter codes above
(for example, change "Miles_per_Gallon"
to "Miles_per_Gallon:Q"
to explicitly specify that it is a quantitative type):
alt.Chart(cars).mark_tick().encode(
x='Miles_per_Gallon:Q',
y='Origin:N',
color='Cylinders:O'
)
Notice how if we change the data type for 'Cylinders'
to ordinal the plot changes.
As you use Altair, it is useful to get into the habit of always specifying these types explicitly, because this is mandatory when working with data loaded from a file or a URL.
Exercise: Adding Explicit Types¶
Following are a few simple charts made with the cars dataset. For each one, try to add explicit types to the encodings (i.e. change "Horsepower"
to "Horsepower:Q"
so that the plot doesn’t change.
Are there any plots that can be made better by changing the type?
alt.Chart(cars).mark_bar().encode(
y='Origin',
x='mean(Horsepower)'
)
alt.Chart(cars).mark_line().encode(
x='Year',
y='mean(Miles_per_Gallon)',
color='Origin'
)
alt.Chart(cars).mark_bar().encode(
y='Cylinders',
x='count()',
color='Origin'
)
alt.Chart(cars).mark_rect().encode(
x='Cylinders',
y='Origin',
color='count()'
)