跳转至

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 + 可视化