简单图表:核心概念

本节的目标是教你创建基本 Altair 图表所需的核心概念;即

  • 数据标记编码:构成 Altair 图表的三个核心部分

  • 编码类型Q (定量), N (标称), O (有序), T (时间),它们决定了编码的视觉表示

  • 分箱和聚合:允许你控制 Altair 中数据表示的各个方面。

对这些核心部分有了深入理解,你就能轻松地在 Altair 中创建各种图表了。

我们首先导入 Altair,并(如有必要)启用相应的渲染器

import altair as alt

一个基本的 Altair 图表

Altair 图表的基本要素是数据标记编码

它们的指定格式如下所示

alt.Chart(data).mark_point().encode(
    encoding_1='column_1',
    encoding_2='column_2',
    # etc.
)

让我们逐一了解这些部分。

数据

Altair 中的数据是围绕 Pandas Dataframe 构建的。本节我们将使用之前看过的汽车数据集,可以使用 vega_datasets 包加载该数据集

from vega_datasets import data
cars = data.cars()

cars.head()
名称 每加仑英里数 气缸数 排量 马力 重量(磅) 加速度 年份 产地
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

Altair 中的数据期望采用 整洁格式(tidy format);换句话说

  • 每一是一个观测值

  • 每一是一个变量

更多信息请参阅 Altair 数据文档

图表(Chart)对象

定义好数据后,你可以实例化 Altair 的基本对象 Chart。本质上,Chart 是一个对象,它知道如何生成一个 JSON 字典来表示数据和可视化编码,这个字典可以发送到笔记本并由 Vega-Lite JavaScript 库渲染。让我们来看看这种 JSON 表示形式是什么样的,只使用数据的第一行

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'}]}}

此时,图表包含数据框的 JSON 格式表示、要使用的标记类型以及每个图表输出中包含的一些元数据。

标记

我们可以决定使用哪种标记来表示我们的数据。在前面的例子中,我们可以选择 point 标记,将每个数据表示为图上的一个点

alt.Chart(cars).mark_point()

结果是一个可视化图,数据中的每一行对应一个点,但这并不是特别有趣:所有点都堆叠在一起了!

再次检查这里的 JSON 输出是有用的

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'}]}}

注意,现在除了数据之外,规范中还包含了关于标记类型的信息。

有许多可用的标记供你使用;一些更常见的如下所示

  • mark_point()

  • mark_circle()

  • mark_square()

  • mark_line()

  • mark_area()

  • mark_bar()

  • mark_tick()

你可以使用 Jupyter 的 Tab 自动补全功能获取 mark_* 方法的完整列表:在任何单元格中输入

alt.Chart.mark_

然后按下 Tab 键即可查看可用选项。

编码

下一步是向图表添加视觉编码通道(简称编码)。编码通道指定了如何将给定的数据列映射到可视化的视觉属性上。以下列出了一些更常用的视觉编码

  • x: x 轴值

  • y: y 轴值

  • color: 标记的颜色

  • opacity: 标记的透明度/不透明度

  • shape: 标记的形状

  • size: 标记的大小

  • row: 分面图网格中的行

  • column: 分面图网格中的列

有关这些编码的完整列表,请参阅文档的编码部分。

可以使用 Chart 对象的 encode() 方法创建视觉编码。例如,我们可以首先将图表的 y 轴映射到 Origin

alt.Chart(cars).mark_point().encode(
    y='Origin'
)

结果是一个一维可视化图,表示 Origin 所取的值,每个类别中的点都堆叠在一起。如上所述,我们可以查看为此可视化图生成的 JSON 数据

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'}]}}

结果与上述相同,但增加了 'encoding' 键,该键指定了可视化通道 (y)、字段名称 (Origin) 和变量类型 (nominal)。我们稍后将讨论这些数据类型。

通过向编码添加另一个通道,可视化图可以变得更有趣:让我们将 Miles_per_Gallon 编码为 x 位置

alt.Chart(cars).mark_point().encode(
    y='Origin',
    x='Miles_per_Gallon'
)

你可以添加任意数量的编码,每个编码映射到数据中的一列。例如,这里我们将根据产地 (Origin) 对点进行着色,并绘制每加仑英里数 (Miles_per_gallon)年份 (Year) 的图

alt.Chart(cars).mark_point().encode(
    color='Origin',
    y='Miles_per_Gallon',
    x='Year'
)

练习:探索数据

既然你已经了解了基础知识(数据、编码、标记),花点时间尝试制作一些图表吧!

特别是,我建议尝试以下各种组合

  • 标记 (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

与搭档一起使用这些选项的不同组合,看看能从数据中学到什么!特别是,思考以下问题

  • 哪些编码适合连续的定量值?

  • 哪些编码适合离散的分类(即标称)值?

大约 10 分钟后,我们将邀请几位志愿者分享他们使用的标记、列和编码的组合。


编码类型

Altair 的核心思想之一是库会为你的数据类型选择好的默认值

Altair 支持的基本数据类型如下所示

数据类型 代码 描述
定量 Q 数值量 (实数)
标称 N 名称 / 无序分类
有序 O 有序分类
时间 T 日期/时间

当你将数据指定为 pandas dataframe 时,这些类型由 Altair 自动确定

当你将数据指定为 URL 时,你必须手动指定每个列的数据类型。

让我们看一个包含汽车数据中三列的简单图表

alt.Chart(cars).mark_tick().encode(
    x='Miles_per_Gallon',
    y='Origin',
    color='Cylinders'
)

问题

  • 什么数据类型最适合 Miles_per_Gallon

  • 什么数据类型最适合 Origin

  • 什么数据类型最适合 Cylinders

让我们使用上面的一字母代码将这些数据类型的缩写添加到我们的规范中(例如,将 "Miles_per_Gallon" 改为 "Miles_per_Gallon:Q" 以显式指定其为定量类型)

alt.Chart(cars).mark_tick().encode(
    x='Miles_per_Gallon:Q',
    y='Origin:N',
    color='Cylinders:O'
)

注意,如果我们把 'Cylinders' 的数据类型改为有序(ordinal),图表就会发生变化。

在使用 Altair 时,养成始终显式指定这些类型的习惯很有用,因为从文件或 URL 加载数据时这是强制性的。

练习:添加显式类型

以下是一些使用汽车数据集制作的简单图表。对于每个图表,尝试为编码添加显式类型(例如,将 "Horsepower" 改为 "Horsepower:Q",这样图表就不会改变。

有没有通过改变类型可以做得更好的图表?

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()'
)