JupyterChart#

在 Vega-Altair 5.1 中引入的 JupyterChart 类使得在图表显示后可以更新图表,并从 Python 访问 交互式图表 的状态。

支持的环境#

JupyterChart 是一个基于 AnyWidget 库构建的 Jupyter 小部件。因此,它与支持第三方 Jupyter 小部件的开发环境和仪表板工具包兼容。经过测试的环境包括

  • 经典 Jupyter Notebook

  • JupyterLab

  • Visual Studio Code

  • Google Colab

  • Voila

注意

如果您在另一个支持 Jupyter 小部件的环境中尝试 JupyterChart,请告诉我们进展如何,以便我们更新此列表。

基本用法#

要创建 JupyterChart,请将常规的 Chart 实例传递给 alt.JupyterChart 构造函数。如果笔记本单元格中的最后一个表达式评估为 JupyterChart 实例,图表将自动显示。例如

import altair as alt
import pandas as pd

source = pd.DataFrame({
    'a': ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I'],
    'b': [28, 55, 43, 91, 81, 53, 19, 87, 52]
})

chart = alt.Chart(source).mark_bar().encode(
    x='a',
    y='b'
)

jchart = alt.JupyterChart(chart)
jchart
Bar chart with letters A through I on the x-axis

更新图表#

JupyterChartchart 属性可以被赋给一个新的图表实例,新图表将立即替换旧图表显示。

jchart.chart = chart.mark_bar(color="crimson", cornerRadius=10)

参数:变量与选择#

交互式图表 中所述,Vega-Altair 丰富的交互语法构建在参数概念之上。特别是,变量参数(存储简单值)和选择参数(将用户交互映射到数据查询)。

JupyterChart 类使变量参数和选择参数都可在 Python 中使用。

变量参数#

JupyterChart 可以访问、观察、设置和链接变量参数。

访问变量参数#

图表的变量参数存储在 JupyterChart 实例的 params 属性中。可以使用常规的属性访问来访问单个命名变量参数的值。这里有一个示例,使用 绑定与小部件 将名为 cutoff 的变量参数绑定到滑块。变量 cutoff 的当前值可以通过 jchart.params.cutoff 访问。

import altair as alt
import pandas as pd
import numpy as np

rand = np.random.RandomState(42)

df = pd.DataFrame({
    'xval': range(100),
    'yval': rand.randn(100).cumsum()
})

slider = alt.binding_range(min=0, max=100, step=1)
cutoff = alt.param(name="cutoff", bind=slider, value=50)
predicate = alt.datum.xval < cutoff

chart = alt.Chart(df).mark_point().encode(
    x='xval',
    y='yval',
    color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue")),
).add_params(
    cutoff
)
jchart = alt.JupyterChart(chart)
jchart

观察变量参数#

params 属性上的 observe 方法可用于注册回调函数,该函数将在参数更改时被调用。在此示例中,注册了一个简单的回调函数来打印参数 cutoff 的值。

def on_cutoff_change(change):
    print(change.new)

jchart.params.observe(on_cutoff_change, ["cutoff"])

设置变量参数#

可以通过对相应的 params 属性赋值来从 Python 更新变量参数的值。这是一个通过对 jchart.params.cutoff 赋值来更新变量参数 cutoff 的示例。

链接变量参数#

因为 params 是一个 traitlet 对象,所以可以使用 ipywidgets 的 link function 将参数绑定到其他 ipywidgets。这是一个将变量参数 cutoff 链接到 ipywidgets IntSlider 值 的示例。

from ipywidgets import IntSlider, link
slider = IntSlider(23, min=0, max=100)
link((slider, "value"), (jchart.params, "cutoff"))
slider

如果 ipywidget 已链接到 Vega-Altair 变量参数,则无需再将该参数绑定到 Vega-Altair 小部件。在这里,上面的示例被更新为仅通过 IntSlider ipywidget 来控制变量 cutoff 的值。

import pandas as pd
import numpy as np

rand = np.random.RandomState(42)

df = pd.DataFrame({
    'xval': range(100),
    'yval': rand.randn(100).cumsum()
})

cutoff = alt.param(name="cutoff", value=50)
predicate = alt.datum.xval < cutoff

chart = alt.Chart(df).mark_point().encode(
    x='xval',
    y='yval',
    color=alt.when(predicate).then(alt.value("red")).otherwise(alt.value("blue"))
).add_params(
    cutoff
)
jchart = alt.JupyterChart(chart)
jchart

选择参数#

JupyterChart 可以访问和观察选择参数。为了从 Python 访问选择,选择参数分为三种类型:点选择、索引选择和区间选择。这些选择类型分别由名为 PointSelectionIndexSelectionIntervalSelection 的 Python 类表示。

这些选择类的实例可作为 JupyterChart 的 selections 属性的属性使用。

点选择#

PointSelection 类用于存储 Vega-Altair 点选择(由 alt.selection_point() 创建)的当前状态,前提是提供了 fieldsencodings 规范。一个常见的例子是带有 encodings=["color"] 并绑定到图例的点选择。

import altair as alt
from vega_datasets import data

source = data.cars()
brush = alt.selection_point(name="point", encodings=["color"], bind="legend")

chart = alt.Chart(source).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color=alt.when(brush).then("Origin:N").otherwise(alt.value("grey")),
).add_params(brush)

jchart = alt.JupyterChart(chart)
jchart

PointSelection 实例可以通过 jchart.selections.point 访问(其中“point”是 alt.selection_pointname 参数的值)。

jchart.selections.point.value 属性包含一个字典列表,其中每个元素代表选择中的一个点。此字典列表可转换为 pandas query 字符串,如下所示

filter = " or ".join([
    " and ".join([
        f"`{col}` == {repr(val)}" for col, val in sel.items()
    ])
    for sel in jchart.selections.point.value
])
source.query(filter)

例如,当选中 Japan 和 Europe 图例条目时,上面的 filter 字符串将评估为 "`Origin` == 'Japan' or `Origin` == 'Europe'",并且 source.query(filter) 表达式将评估为包含 source 中属于选择的行的 pandas DataFrame

索引选择#

IndexSelection 类用于存储 Vega-Altair 点选择(由 alt.selection_point() 创建)的当前状态,前提是未提供 fieldsencodings 规范。在这种情况下,选择的 value 属性是选定行的索引列表。这些索引可用于 pandas DataFrame 的 iloc 属性,以提取输入 DataFrame 中选定的行。

import altair as alt
from vega_datasets import data

source = data.cars()
brush = alt.selection_point(name="point")

chart = alt.Chart(source).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color=alt.when(brush).then("Origin:N").otherwise(alt.value("grey")),
).add_params(brush)

jchart = alt.JupyterChart(chart)
jchart

警告

返回的索引仅对应于不包含聚合的图表的输入 DataFrame。如果图表包含聚合,则 alt.selection_point 规范应包含 fieldsencodings 参数之一,这将导致 JupyterChart 包含 PointSelection 而非 IndexSelection

区间选择#

IntervalSelection 类用于存储 Vega-Altair 区间选择(由 alt.selection_interval() 创建)的当前状态。在这种情况下,选择的 value 属性是一个从列名到选择区间的字典

import altair as alt
from vega_datasets import data

source = data.cars()
brush = alt.selection_interval(name="interval")

chart = alt.Chart(source).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color=alt.when(brush).then("Cylinders:O").otherwise(alt.value("grey")),
).add_params(brush)

jchart = alt.JupyterChart(chart)
jchart

选择字典可以转换为 pandas query 字符串,如下所示

filter = " and ".join([
    f"{v[0]} <= `{k}` <= {v[1]}"
    for k, v in jchart.selections.interval.value.items()
])
source.query(filter)

例如,当 x 选择范围是 120 到 160 且 y 选择范围是 25 到 35 时,jchart.selections.interval.value 将是 {'Horsepower': [120, 160], 'Miles_per_Gallon': [25, 30]}filter 字符串将是 "120 <= `Horsepower` <= 160 and 25 <= `Miles_per_Gallon` <= 35",并且 source.query(filter) 表达式将评估为包含 source 中属于选择的行的 pandas DataFrame

观察选择#

与变量参数一样,可以通过在 selections 属性上使用 observe 方法来注册回调函数,该函数将在选择更改时被调用。这是一个示例,监听区间选择的变化,然后使用选择值过滤输入 DataFrame 并显示其 HTML 表示形式。使用 ipywidgets VBox 将图表和 HTML 表格组合在列布局中。

import ipywidgets
from IPython.display import display
from ipywidgets import HTML, VBox

import altair as alt
from vega_datasets import data

source = data.cars()
brush = alt.selection_interval(name="brush")

chart_widget = alt.JupyterChart(alt.Chart(source).mark_point().encode(
    x='Horsepower:Q',
    y='Miles_per_Gallon:Q',
    color=alt.when(brush).then("Cylinders:O").otherwise(alt.value("grey")),
).add_params(brush))

table_widget = HTML(value=source.iloc[:0].to_html())

def on_select(change):
    sel = change.new.value
    if sel is None or 'Horsepower' not in sel:
        filtered = source.iloc[:0]
    else:
        filter_query = (
            f"{sel['Horsepower'][0]} <= `Horsepower` <= {sel['Horsepower'][1]} and "
            f"{sel['Miles_per_Gallon'][0]} <= `Miles_per_Gallon` <= {sel['Miles_per_Gallon'][1]}"
        )
        filtered = source.query(filter_query)

    table_widget.value = filtered.to_html()

chart_widget.selections.observe(on_select, ["brush"])

VBox([chart_widget, table_widget])

离线使用#

默认情况下,JupyterChart 小部件从 CDN 位置动态加载其 JavaScript 依赖项,这需要活动的互联网连接。从 Altair 5.3 开始,JupyterChart 支持从 vl-convert-python 包加载其 JavaScript 依赖项,从而实现离线使用。

离线模式通过 JupyterChart.enable_offline 类方法启用。

import altair as alt
alt.JupyterChart.enable_offline()

只需调用一次此方法,之后所有显示的 JupyterCharts 都将在离线模式下运行。

可以通过向同一方法传递 offline=False 来禁用离线模式。

import altair as alt
alt.JupyterChart.enable_offline(offline=False)

限制#

设置选择#

目前尚无法从 Python 设置选择状态。