分层和多视图图表#

除了基本的 Chart 对象外,Altair 还提供了多种复合图表类型,可用于创建堆叠图、分层图、分面图和重复图表。它们总结在下表中

函数形式

运算符形式

参考

LayerChart

alt.layer(chart1, chart2)

chart1 + chart2

分层图表

HConcatChart

alt.hconcat(chart1, chart2)

chart1 | chart2

水平串联

VConcatChart

alt.vconcat(chart1, chart2)

chart1 & chart2

垂直串联

方法形式

参考

RepeatChart

chart.repeat(row, column)

重复图表

FacetChart

chart.facet(facet, row, column)

分面图表

分层图表#

分层图表允许您在同一组坐标轴上叠加两个不同的图表。例如,当您希望为相同数据绘制多个标记时,它们非常有用;例如

import altair as alt
from vega_datasets import data

stocks = data.stocks.url

base = alt.Chart(stocks).encode(
    x='date:T',
    y='price:Q',
    color='symbol:N'
).transform_filter(
    alt.datum.symbol == 'GOOG'
)

base.mark_line() + base.mark_point()

这里我们使用了 + 运算符来创建分层图表;或者,我们可以使用 alt.layer 函数,该函数接受任意数量的图表作为其参数

alt.layer(
  base.mark_line(),
  base.mark_point(),
  base.mark_rule()
).interactive()

这两种模式的输出都是一个 LayerChart 对象,它具有类似于 Chart 对象的属性和方法。

图层顺序#

在分层图表中,图层的顺序由它们指定的顺序决定。例如,当使用 layer1 + layer2alt.layer(layer1, layer2) 创建图表时,layer1 将出现在 layer2 下方,并且 layer2 可能会遮挡 layer1 的标记。

例如,考虑下面的图表,我们在热力图的顶部绘制点

import altair as alt
from vega_datasets import data

source = data.movies.url

heatmap = alt.Chart(source).mark_rect().encode(
    alt.X('IMDB_Rating:Q').bin(),
    alt.Y('Rotten_Tomatoes_Rating:Q').bin(),
    alt.Color('count()').scale(scheme='greenblue')
)

points = alt.Chart(source).mark_circle(
    color='black',
    size=5,
).encode(
    x='IMDB_Rating:Q',
    y='Rotten_Tomatoes_Rating:Q',
)

heatmap + points

如果我们将两个图层颠倒顺序,则点将先绘制,然后会被热力图标记遮挡

points + heatmap

如果您在创建分层图表时没有看到预期的输出,请确保您正确地对图层进行了排序。

水平串联#

将两个图并排放置通常可以使用 HConcatChart 对象完成,该对象可以使用 hconcat 函数或 | 运算符创建。

例如,这里有一个散点图,它与显示点分布的直方图进行了串联

import altair as alt
from vega_datasets import data

iris = data.iris.url

chart1 = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    height=300,
    width=300
)

chart2 = alt.Chart(iris).mark_bar().encode(
    x='count()',
    y=alt.Y('petalWidth:Q').bin(maxbins=30),
    color='species:N'
).properties(
    height=300,
    width=100
)

chart1 | chart2

本例使用了 | 运算符,但也可以类似地使用 hconcat() 函数创建

alt.hconcat(chart1, chart2)

这两种模式的输出都是一个 HConcatChart 对象,它具有许多与 Chart 对象相同的顶层方法和属性。

最后,请记住,对于某些类型的水平串联图表,其中每个面板只修改可视化效果的一个方面,重复图表和分面图表更方便(更多解释请参见重复图表分面图表)。

垂直串联#

与上面的水平串联类似,Altair 通过 vconcat() 函数或 & 运算符提供垂直串联。

例如,这里我们将同一数据的两个视图垂直串联起来,并使用 brush 选择来添加交互性

import altair as alt
from vega_datasets import data

source = data.sp500.url

brush = alt.selection_interval(encodings=['x'])

base = alt.Chart(source).mark_area().encode(
    x = 'date:T',
    y = 'price:Q'
).properties(
    width=600,
    height=200
)

upper = base.encode(alt.X('date:T').scale(domain=brush))

lower = base.properties(
    height=60
).add_params(brush)

alt.vconcat(upper, lower)

请注意,我们也可以使用 upper & lower,而不是更冗长的 alt.vconcat(upper, lower)

与水平串联图表一样,请记住,对于每个面板中只有一个数据分组或编码发生变化的串联,使用重复图表分面图表会更有效率。

重复图表#

RepeatChart 对象为特定类型的水平或垂直串联提供了一个方便的接口,其中串联面板之间唯一的区别是修改了一个或多个编码

例如,假设您想创建一个多面板散点图来显示多维数据集的不同投影。让我们先使用 hconcatvconcat 手动创建这样的图表,然后再展示如何使用 repeat 更有效地构建图表

import altair as alt
from vega_datasets import data

iris = data.iris.url

base = alt.Chart().mark_point().encode(
    color='species:N'
).properties(
    width=200,
    height=200
).interactive()

chart = alt.vconcat(data=iris)
for y_encoding in ['petalLength:Q', 'petalWidth:Q']:
    row = alt.hconcat()
    for x_encoding in ['sepalLength:Q', 'sepalWidth:Q']:
        row |= base.encode(x=x_encoding, y=y_encoding)
    chart &= row
chart

在本例中,我们显式循环遍历不同的 x 和 y 编码,以创建显示数据不同视图的 2 x 2 网格图表。代码很直观,虽然有点冗长。

RepeatChart 模式通过 Chart.repeat() 方法访问,使这类图表更容易生成

import altair as alt
from vega_datasets import data
iris = data.iris.url

alt.Chart(iris).mark_point().encode(
    alt.X(alt.repeat("column"), type='quantitative'),
    alt.Y(alt.repeat("row"), type='quantitative'),
    color='species:N'
).properties(
    width=200,
    height=200
).repeat(
    row=['petalLength', 'petalWidth'],
    column=['sepalLength', 'sepalWidth']
).interactive()

这里的关键是 Chart.repeat() 方法:它允许您为行和/或列指定一组编码,这些编码可以在图表的编码规范中使用 alt.repeat('row')alt.repeat('column') 来引用。

使用 repeat 方法的另一种选择是用于分层。在这里,US_GrossWorldwide_Gross 列使用 alt.repeat('layer')y 轴上分层显示

import altair as alt
from vega_datasets import data

source = data.movies()

alt.Chart(source).mark_line().encode(
    x=alt.X("IMDB_Rating").bin(),
    y=alt.Y(alt.repeat('layer')).aggregate('mean').title("Mean of US and Worldwide Gross"),
    color=alt.ColorDatum(alt.repeat('layer'))
).repeat(layer=["US_Gross", "Worldwide_Gross"])

目前 repeat 只能是编码(例如,不能是数据转换),但 Vega-Lite 社区正在讨论未来使这种模式更通用。

分面图表#

与重复图表一样,分面图表提供了数据集的多个视图。但不同之处在于,它不是为不同的编码设置不同的面板,而是为不同的数据子集设置不同的面板。例如,鸢尾花数据集中每种花都对应一个面板。

这也被称为 小型倍数图、棚架图、格状图、网格图或面板图。

我们可以通过结合过滤转换和水平串联来手动实现这一点

import altair as alt
from vega_datasets import data

iris = data.iris.url

base = alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    width=160,
    height=160
)

chart = alt.hconcat()
for species in ['setosa', 'versicolor', 'virginica']:
    chart |= base.transform_filter(alt.datum.species == species)
chart

重复图表的手动方法一样,这很直接,虽然有点冗长。

使用 .facet 会更简洁一些

alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N'
).properties(
    width=180,
    height=180
).facet(
    column='species:N'
)

对于这类简单图表,还有一个 column 编码通道可以获得相同的结果

alt.Chart(iris).mark_point().encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color='species:N',
    column='species:N'
).properties(
    width=180,
    height=180
)

使用 .facet 的优点在于它可以创建更复杂的复合图表的分面视图。例如,这是一个带有悬停选择的分层图表的分面视图

hover = alt.selection_point(on='pointerover', nearest=True, empty=False)
when_hover = alt.when(hover)

base = alt.Chart(iris).encode(
    x='petalLength:Q',
    y='petalWidth:Q',
    color=when_hover.then("species:N").otherwise(alt.value("lightgray"))
).properties(
    width=180,
    height=180,
)

points = base.mark_point().add_params(hover)

text = base.mark_text(dy=-5).encode(
    text="species:N",
    opacity=when_hover.then(alt.value(1)).otherwise(alt.value(0)),
)

(points + text).facet("species:N")

尽管上面的每个示例都按列对数据进行了分面,但也支持按行(或按行列)进行分面。