Observable 数据探索
Observable 是数据可视化大师 Mike Bostock(D3.js 作者)创建的下一代数据探索平台,用响应式 Notebook 写 JavaScript 代码,内置 D3 和 Observable Plot,还有全新的 Framework 静态站点生成器和 Canvas 协作白板,是前端数据可视化的最前沿工具。
核心知识点
| 知识点 | 说明 |
|---|
| 工具定位 | 数据探索与可视化平台 |
| 创建者 | Mike Bostock(D3.js 作者) |
| 核心产品 | Observable Notebooks + Framework + Canvases |
| 核心优势 | 响应式编程、D3/Plot 内置、协作分享、免费 |
| 最新动态 | Notebooks 2.0(本地文件格式)+ Canvases(2025) |
| 编程语言 | JavaScript(支持 SQL 单元格) |
安装配置
Observable Framework(本地开发)
# 安装 Observable Framework(静态站点生成器)
npm init @observablehq # 交互式创建项目
# 或
npx @observablehq/framework create my-dashboard # 快速创建
cd my-dashboard
npm run dev # 启动开发服务器
npm run build # 构建静态站点
Observable Notebooks(在线使用)
# 直接访问 observablehq.com
# 1. 注册免费账号
# 2. 点击 New Notebook
# 3. 开始写代码,所见即所得
基本使用
1. Observable Notebook 基础
// Observable Notebook 中每个单元格都是响应式的
// 改变一个变量,所有依赖它的单元格自动更新
// 定义数据
data = [
{ species: "Bacteroidetes", abundance: 35 }, // 物种丰度数据
{ species: "Firmicutes", abundance: 28 },
{ species: "Proteobacteria", abundance: 15 },
{ species: "Actinobacteria", abundance: 12 }
]
// 用 Observable Plot 画图(比 D3 简单很多)
Plot.barY(data, { // 垂直柱状图
x: "species", // X 轴
y: "abundance", // Y 轴
fill: "species", // 颜色
tip: true // 悬停提示
}).plot({ marginBottom: 60 }) // 渲染
2. Observable Plot(高层可视化 API)
// Observable Plot 是 D3 的高层封装,几行代码出图
// 散点图
Plot.dot(data, {
x: "shannon", // X 轴:Shannon 指数
y: "simpson", // Y 轴:Simpson 指数
fill: "group", // 按组着色
r: 5, // 点半径
tip: true // 悬停提示
}).plot()
// 折线图
Plot.lineY(timeData, {
x: "date", // 时间轴
y: "diversity", // 数值轴
stroke: "group" // 按组画线
}).plot()
// 箱线图
Plot.boxY(data, {
x: "group", // 分组
y: "abundance" // 数值
}).plot()
// 热力图
Plot.cell(matrix, {
x: "sample", // X 轴
y: "gene", // Y 轴
fill: "expression", // 填充色映射表达量
tip: true
}).plot({ color: { scheme: "RdBu" } }) // 红蓝配色
3. 交互式输入
// Observable 内置交互组件(viewof 关键字)
viewof threshold = Inputs.range([0, 100], { // 范围滑块
step: 1, value: 10,
label: "丰度阈值"
})
viewof selectedGroup = Inputs.select( // 下拉选择
["健康", "疾病", "全部"],
{ label: "选择组别", value: "全部" }
)
// 过滤数据(自动响应式更新)
filteredData = data.filter(d =>
d.abundance > threshold && // 根据滑块过滤
(selectedGroup === "全部" || d.group === selectedGroup) // 根据下拉过滤
)
// 图表自动更新
Plot.barY(filteredData, {
x: "species", y: "abundance", fill: "species"
}).plot()
4. SQL 查询
// Observable 支持直接写 SQL 查询数据
// 数据来源:CSV、Parquet、DuckDB、PostgreSQL 等
// 从 CSV 文件查询
db = DuckDBClient.of({ abundance: FileAttachment("abundance.csv") })
// SQL 单元格
result = db.sql`
SELECT species, AVG(abundance) as avg_abundance
FROM abundance
WHERE abundance > ${threshold}
GROUP BY species
ORDER BY avg_abundance DESC
`
// 用查询结果画图
Plot.barY(result, {
x: "species", y: "avg_abundance"
}).plot()
高级用法
1. Observable Framework(构建数据仪表盘站点)
<!-- docs/index.md — Framework 用 Markdown + JavaScript -->
# 菌群分析报告
```js
// 数据加载器(构建时运行 Python/R/Shell)
const data = FileAttachment("data/abundance.csv").csv({typed: true});
物种丰度分布
Plot.barY(data, {
x: "species", y: "abundance", fill: "species", tip: true
}).plot({width})
多样性指数
const threshold = view(Inputs.range([0, 50], {value: 10, label: "最小丰度"}));
const filtered = data.filter(d => d.abundance > threshold);
Plot.dot(filtered, {x: "shannon", y: "simpson", fill: "group"}).plot({width})
```bash
# 数据加载器(任何语言!构建时执行)
# docs/data/abundance.csv.py ← Python 数据加载器
import pandas as pd
df = pd.read_csv("raw_data.csv")
df.to_csv("/dev/stdout", index=False) # 输出到 stdout
2. 地理可视化
// Observable + D3 做地理可视化
world = fetch("https://unpkg.com/world-atlas/countries-110m.json")
.then(r => r.json()) // 加载世界地图数据
Plot.geo(world, {
fill: d => sampleCount.get(d.properties.name) || 0, // 按样本数着色
tip: d => d.properties.name // 悬停显示国家名
}).plot({ projection: "equal-earth", color: { scheme: "Blues" } })
3. 分享与嵌入
// Observable Notebook 可以直接分享
// 1. 点击 Publish → 公开链接
// 2. 嵌入到任何网页:
// <iframe width="100%" height="500"
// src="https://observablehq.com/embed/@user/notebook"></iframe>
// Framework 构建的站点可以部署到任何静态托管
// npm run build → 输出到 dist/ → 部署到 GitHub Pages / Netlify / Vercel
常见报错与解决
| 报错信息 | 原因 | 解决方法 |
|---|
ReferenceError: x is not defined | 单元格之间依赖错误 | 检查变量名拼写和定义顺序 |
| 图表不渲染 | Plot 语法错误 | 参考 Observable Plot 文档 |
| 数据加载失败 | FileAttachment 路径错 | 确认文件已上传或路径正确 |
| Framework 构建慢 | 数据加载器耗时 | 优化数据加载脚本 |
速查表
// ===== Observable 速查表 =====
// Observable Plot(高层 API)
Plot.barY(data, {x, y, fill}) // 柱状图
Plot.dot(data, {x, y, fill, r}) // 散点图
Plot.lineY(data, {x, y, stroke}) // 折线图
Plot.boxY(data, {x, y}) // 箱线图
Plot.cell(data, {x, y, fill}) // 热力图
Plot.areaY(data, {x, y, fill}) // 面积图
Plot.geo(topology) // 地理图
// 交互输入
viewof x = Inputs.range([min, max]) // 滑块
viewof x = Inputs.select(options) // 下拉
viewof x = Inputs.radio(options) // 单选
viewof x = Inputs.checkbox(options) // 多选
viewof x = Inputs.search(data) // 搜索
viewof x = Inputs.table(data) // 交互表格
// 数据加载
FileAttachment("file.csv").csv() // CSV
FileAttachment("file.json").json() // JSON
FileAttachment("file.parquet").parquet() // Parquet
// SQL(DuckDB)
db = DuckDBClient.of({table: data})
result = db.sql`SELECT * FROM table`
// Framework(静态站点)
npm init @observablehq // 创建项目
npm run dev // 开发
npm run build // 构建
// 三个产品对比
// Notebooks: 在线协作探索,响应式
// Framework: 本地开发,构建静态仪表盘站点
// Canvases: 协作白板,2D 无限画布,SQL + 可视化