Interact + Vega-Lite — live chart

用 template vega-lite: 把 Interact controls 接到 Vega-Lite chart——拖滑块 / 切 toggle,chart 实时重绘。Observable / Streamlit 级的体验,但全声明、零 JS。


When to use

  • ✅ 参数驱动的曲线 / 分布图(拖 slider 改函数参数看曲线变形)
  • ✅ 数据探索(toggle 切维度 / 切聚合 / 切色彩编码)
  • ✅ 教学可视化(三角函数 / 概率密度 / 物理仿真)
  • ✅ 财务 / 工程 / 数据科学(任何需要 "改参数 → 立刻看趋势" 的)
  • ❌ 极简结果展示(用 text template 即可)
  • ❌ 静态图表(直接 ```vega-lite 不需 interact 包装)
  • ❌ 需要复杂 D3 自定义 → vega-lite 表达力有上限,超过用 SVG scenes

Basic syntax

\```interact
slider freq 1 10 3 0.1
template vega-lite:
{
  "data": {"sequence": {"start": 0, "stop": 6.28, "step": 0.05, "as": "t"}},
  "transform": [{"calculate": "sin({freq} * datum.t)", "as": "y"}],
  "mark": "line",
  "encoding": {
    "x": {"field": "t", "type": "quantitative"},
    "y": {"field": "y", "type": "quantitative"}
  }
}
\```

template vega-lite: 后是 Vega-Lite JSON spec——Rho 用控件值替换 {var},再喂给 vega-embed 渲染。

渲染:滑块 + chart。拖 freq → sin({freq} * t) 重算 → 曲线变形。

依赖:要看 chart 渲染,project 需要 vega-embed:npm install vega-embed。


占位符在 vega-lite spec 里的位置

{var} 可以出现在 JSON 字符串值里,跟其他地方一样实时替换:

{
  "transform": [
    {"calculate": "{amplitude} * sin({frequency} * datum.t)", "as": "y"}
  ],
  "encoding": {
    "y": {"field": "y", "type": "quantitative", "scale": {"domain": [-{maxY}, {maxY}]}}
  }
}

替换发生在 JSON 解析之前,所以 {var} 可在数字字段(域、轴范围、scale 上下界)或字符串字段(color hex / 标题)里用。

⚠️ 数字字段用 {var}(不带引号)—— 替换后是裸数字。 字符串字段用 "{var}"(带引号)—— 替换后是字符串。


Examples

Example 1:正弦波 + 频率 / 振幅

\```interact
slider freq 0.5 5 1 0.1
slider amp 0.1 2 1 0.1
template vega-lite:
{
  "width": 400,
  "height": 200,
  "data": {"sequence": {"start": 0, "stop": 6.28, "step": 0.05, "as": "t"}},
  "transform": [
    {"calculate": "{amp} * sin({freq} * datum.t)", "as": "y"}
  ],
  "mark": "line",
  "encoding": {
    "x": {"field": "t", "type": "quantitative", "title": "时间 t"},
    "y": {"field": "y", "type": "quantitative", "title": "振幅", "scale": {"domain": [-2, 2]}}
  }
}
\```

渲染:两个滑块 + 正弦波 chart。改 amp 看振幅伸缩,改 freq 看波密度变。{amp} {freq} 在 calculate 里被替换。

Example 2:复利增长曲线

\```interact
slider principal 1000 100000 10000 1000
slider rate 0 0.15 0.05 0.01
slider years 5 50 20 1
template vega-lite:
{
  "width": 500,
  "height": 250,
  "data": {"sequence": {"start": 0, "stop": {years}, "step": 1, "as": "year"}},
  "transform": [
    {"calculate": "{principal} * pow(1+{rate}, datum.year)", "as": "value"}
  ],
  "mark": {"type": "line", "point": true},
  "encoding": {
    "x": {"field": "year", "type": "quantitative", "title": "年"},
    "y": {"field": "value", "type": "quantitative", "title": "金额 ($)"}
  }
}
\```

渲染:3 滑块 + 复利曲线,{years} 在 sequence 的 stop 里被替换 → x 轴范围跟随 years 变。

Example 3:概率密度(正态分布)

\```interact
slider mean -5 5 0 0.1
slider stddev 0.1 3 1 0.1
template vega-lite:
{
  "width": 500,
  "height": 250,
  "data": {"sequence": {"start": -10, "stop": 10, "step": 0.1, "as": "x"}},
  "transform": [
    {"calculate": "(1 / ({stddev} * sqrt(2 * 3.14159))) * exp(-pow(datum.x - {mean}, 2) / (2 * pow({stddev}, 2)))", "as": "p"}
  ],
  "mark": "area",
  "encoding": {
    "x": {"field": "x", "type": "quantitative", "title": "x"},
    "y": {"field": "p", "type": "quantitative", "title": "P(x)"}
  }
}
\```

渲染:拖 mean 看曲线左右移,拖 stddev 看曲线宽窄变。

Example 4:toggle 切图表类型

\```interact
toggle showArea false
template vega-lite:
{
  "data": {
    "values": [
      {"month": "Jan", "v": 100},
      {"month": "Feb", "v": 130},
      {"month": "Mar", "v": 95},
      {"month": "Apr", "v": 180},
      {"month": "May", "v": 220}
    ]
  },
  "mark": "{showArea ? "area" : "line"}",
  "encoding": {
    "x": {"field": "month", "type": "ordinal"},
    "y": {"field": "v", "type": "quantitative"}
  }
}
\```

渲染:toggle 切 line 和 area。"{showArea ? ... : ...}" 在字符串字段做条件替换。


Plain-text fallback behavior

```interact 含 template vega-lite: 在不支持 reader 里:

\```interact
slider freq 1 10 3 0.1
template vega-lite:
{
  ... full vega-lite spec ...
}
\```

→ 显示为代码块——读者看到 sliders + 完整 vega-lite spec(含 {var} 占位符)。有经验的读者能猜出原来是 chart;普通读者看到的是结构化数据。

具体各 reader:

Reader 渲染效果
Rho(含 vega-embed) 完整可交互 chart
Rho(缺 vega-embed) "Chart needs vega-embed" 提示
GitHub web 代码块(spec 全文可见)
Obsidian 同上
VS Code 默认预览 同上
cat / less plain text

Common pitfalls

1. JSON 语法错

template vega-lite:
{
  "mark": "line"
  "encoding": { ... }    ← ❌ 缺逗号
}

→ vega-embed 抛 JSON parse error,chart 不渲染。vega-lite spec 是严格 JSON——双引号 key、value 间逗号、严格 quote。

推荐:在外部 JSON 编辑器(VS Code)写好再贴进来,或用 vega-lite Editor (https://vega.github.io/editor/) 验证。

2. 占位符用错位置

template vega-lite:
{
  "mark": {freq},                 ← ❌ {freq} 替换后变成数字 3,但这里要字符串 "line"
  ...
}

→ 替换后产生非法 JSON。根据上下文确定 {var} 是否要带引号。

3. 数字字段误带引号

template vega-lite:
{
  "encoding": {
    "y": {"scale": {"domain": [-"{maxY}", "{maxY}"]}}     ← ❌ 替换后是字符串 "10",scale.domain 要数字
  }
}

→ 改成 [-{maxY}, {maxY}](不带引号)。

4. transform.calculate 表达式 syntax 跟 mini DSL 不同

template vega-lite:
{
  "transform": [
    {"calculate": "if (datum.x > 0) datum.x else 0", "as": "y"}    ← ❌ vega 不支持 if/else 语句
  ]
}

→ vega-lite 的 calculate 用 vega expression 语法(不是 mini DSL)。三元式 cond ? a : b 支持,函数 sin / cos / pow / sqrt 等支持。详见 vega expression 文档。

5. data.sequence 范围太大

"data": {"sequence": {"start": 0, "stop": 1000000, "step": 0.001, "as": "x"}}

→ 1 billion 个数据点 → 浏览器卡死。控制点数 < 5000。曲线 < 500 点已经看着平滑。

6. chart 跟其他 chart 共用 data

\```interact namespace finance
slider rate 0 0.15 0.05 0.01
\```

\```interact namespace finance
template vega-lite:
{ ... uses {rate} ... }
\```

\```interact namespace finance
template vega-lite:
{ ... uses {rate} ... }
\```

✅ 合法 + 推荐——多个 chart 共用同一组 sliders,详见 Shared state。

7. 没装 vega-embed

\```interact
template vega-lite:
{ ... }
\```

→ 项目没 install vega-embed 时 chart 不渲染(显示提示或空白)。用 chart 必装 npm install vega-embed。


Vega-Lite spec 常用片段速查

折线图

{
  "data": {...},
  "mark": "line",
  "encoding": {
    "x": {"field": "x", "type": "quantitative"},
    "y": {"field": "y", "type": "quantitative"}
  }
}

柱状图

{
  "data": {...},
  "mark": "bar",
  "encoding": {
    "x": {"field": "category", "type": "ordinal"},
    "y": {"field": "value", "type": "quantitative"}
  }
}

散点图

{
  "data": {...},
  "mark": "circle",
  "encoding": {
    "x": {"field": "x", "type": "quantitative"},
    "y": {"field": "y", "type": "quantitative"},
    "size": {"field": "z", "type": "quantitative"}
  }
}

参数化曲线(最常用)

{
  "data": {"sequence": {"start": 0, "stop": 10, "step": 0.1, "as": "x"}},
  "transform": [{"calculate": "...{your_expr}...", "as": "y"}],
  "mark": "line",
  "encoding": {
    "x": {"field": "x"},
    "y": {"field": "y", "type": "quantitative"}
  }
}

详见 Vega-Lite 完整文档。


See also