Altair 内部#
本节将提供关于 Altair API 如何与 Vega-Lite 可视化规范相关联的详细信息,以及如何利用这些知识更有效地使用该软件包。
首先,重要的是要认识到,如果剥离其核心功能,Altair 本身无法渲染可视化。Altair 是一个 API,它做一件定义非常明确的事情
Altair 提供了一个用于生成已验证的 Vega-Lite 规范的 Python API
仅此而已。要将这些规范转化为实际的可视化,需要一个正确配置的前端,但严格来说,这种渲染通常不受 Altair 软件包控制。
Altair 图表到 Vega-Lite 规范#
由于 Altair 根本上是关于构建图表规范的,任何图表对象的核心功能是 to_dict()
和 to_json()
方法,它们分别将图表规范输出为 Python 字典或 JSON 字符串。例如,这是一个简单的散点图,我们可以从中输出 JSON 表示
import altair as alt
from vega_datasets import data
chart = alt.Chart(data.cars.url).mark_point().encode(
x='Horsepower:Q',
y='Miles_per_Gallon:Q',
color='Origin:N',
).configure_view(
continuousHeight=300,
continuousWidth=300,
)
print(chart.to_json(indent=2))
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.20.1.json",
"config": {
"view": {
"continuousHeight": 300,
"continuousWidth": 300
}
},
"data": {
"url": "https://cdn.jsdelivr.net.cn/npm/vega-datasets@v1.29.0/data/cars.json"
},
"encoding": {
"color": {
"field": "Origin",
"type": "nominal"
},
"x": {
"field": "Horsepower",
"type": "quantitative"
},
"y": {
"field": "Miles_per_Gallon",
"type": "quantitative"
}
},
"mark": {
"type": "point"
}
}
在返回字典或 JSON 输出之前,Altair 使用 jsonschema 包根据 Vega-Lite 模式对其进行验证。Vega-Lite 模式定义了 Vega-Lite 图表规范中可以出现的有效属性和值。
有了 JSON 模式,就可以将其传递给一个知道如何读取规范并渲染图表(规范描述的图表)的库,例如 Vega-Embed,结果就是下面的可视化效果
点击显示代码
chart
无论何时你在 JupyterLab、Jupyter notebook 或其他前端中使用 Altair,都是前端扩展从 Altair 图表对象中提取 JSON 输出,并将该规范传递给相应的渲染代码。
Altair 的低级对象结构#
Altair 中使用的标准 API 方法(例如 mark_point()
、encode()
、configure_*()
、transform_*()
等)是包装低级 API 的更高级别的便捷函数。该低级 API 本质上是一个 Python 对象层次结构,它反映了 JSON 模式定义的结构。
例如,我们可以选择避免使用便捷方法,而是直接使用这些低级对象类型来构建上面的图表
alt.Chart(
data=alt.UrlData(
url='https://vega.github.io/vega-datasets/data/cars.json'
),
mark='point',
encoding=alt.FacetedEncoding(
x=alt.PositionFieldDef(
field='Horsepower',
type='quantitative'
),
y=alt.PositionFieldDef(
field='Miles_per_Gallon',
type='quantitative'
),
color=alt.StringFieldDefWithCondition(
field='Origin',
type='nominal'
)
),
config=alt.Config(
view=alt.ViewConfig(
continuousHeight=300,
continuousWidth=300
)
)
)
这种低级方法比典型的惯用方法来创建 Altair 图表要冗长得多,但它更清楚地说明了 Altair 的 Python 对象结构与 Vega-Lite 的模式定义结构之间的映射关系。
Altair 的一个很好的特点是,这种低级对象层次结构不是手动构建的,而是使用 generate_schema_wrapper.py
脚本从 Vega-Lite 模式程序化生成的,你可以在Altair 的仓库中找到该脚本。这种代码的自动生成将 Vega-Lite 模式中的描述传播到 Python 类的文档字符串中,然后 Altair 文档中的 API 参考也由此自动生成。这意味着随着 Vega-Lite 模式的发展,Altair 可以非常快速地更新,只有更高级别的图表方法需要手动更新。
将 Vega-Lite 转换为 Altair#
有了这些知识并在实践中积累一些经验,从 Vega-Lite 规范构建一个 Altair 图表就相当直接了。例如,考虑 Vega-Lite 文档中的简单条形图示例,它有以下 JSON 规范
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
在最低层面,我们可以使用 from_json()
类方法从这段 Vega-Lite JSON 字符串构建一个 Altair 图表对象
import altair as alt
alt.Chart.from_json("""
{
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
}
""")
同样,如果你有等同于 JSON 字符串的 Python 字典,你可以使用 from_dict()
方法构建图表对象
import altair as alt
alt.Chart.from_dict({
"$schema": "https://vega.github.io/schema/vega-lite/v5.json",
"description": "A simple bar chart with embedded data.",
"data": {
"values": [
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
]
},
"mark": {"type": "bar"},
"encoding": {
"x": {"field": "a", "type": "ordinal"},
"y": {"field": "b", "type": "quantitative"}
}
})
再多花点力气并巧妙地进行复制粘贴,我们可以将此手动转换为更符合习惯的 Altair 代码来实现同一图表,包括从数据值构建一个 pandas 数据框
import altair as alt
import pandas as pd
data = pd.DataFrame.from_records([
{"a": "A","b": 28}, {"a": "B","b": 55}, {"a": "C","b": 43},
{"a": "D","b": 91}, {"a": "E","b": 81}, {"a": "F","b": 53},
{"a": "G","b": 19}, {"a": "H","b": 87}, {"a": "I","b": 52}
])
alt.Chart(data).mark_bar().encode(
x='a:O',
y='b:Q'
)
关键在于认识到 'encoding'
属性通常使用 encode()
方法设置,编码类型通常通过速记类型代码计算得出,'transform'
和 'config'
属性来自 transform_*()
和 configure_*()
方法,依此类推。
这种方法是 Altair 贡献者构建 Altair 示例库中许多初始示例的过程,他们从 Vega-Lite 示例库中汲取灵感。熟悉 Altair 和 Vega-Lite 在这个层面的映射关系,对于在 Altair 文档薄弱或不完整的地方利用 Vega-Lite 文档非常有益。