分层和多视图图表#

除了基本的 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 的图表网格,展示数据的不同视图。代码是直接的,虽然有点冗长。

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

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_Grossy 轴上使用 alt.repeat('layer') 进行分层:

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")

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