Data Visualization First Steps

不必再盯着电子表格里的数据了,你可以把这些数据变成可视化且可交互的内容!这样能让你快速探索信息并解答相关问题。在本课程中,你将使用Observable Plot库动手构建项目。你会制作可视化图表或图形,添加交互功能,并借助JavaScript以及React等框架,将你的图表嵌入到各类网站中。通过学习这些基础知识,你将掌握继续深入学习d3.js等更复杂工具所需的技能!

0-introduction

这个课程是关于数据可视化基础的讲座,由 Angana Vakil 主讲。她是 Hisura 的高级开发者倡导者和 Observable 大使,曾经是 Observable 团队成员。

课程概述

  • 课程材料以 Observable notebooks 形式提供
  • 访问地址:observablehq.com/@anjana/data-visualization-first-steps
  • 所有工作都在浏览器中的 Observable 环境内完成
  • 不需要 Observable 账户也可以参与,但建议注册以保存工作

Observable 平台介绍

Observable 是一个浏览器内的编码环境,专为数据可视化设计:

  • 使数据可视化变得快速简单
  • 支持实时编码和即时反馈
  • 提供交互式编程体验

讲师背景

Angana 来自旧金山,现在在明尼阿波利斯进行现场讲座。她在几年前加入 Observable 时开始接触数据可视化,学习了数据可视化对开发者的帮助。她将分享从数据可视化新手到熟练使用者的学习历程。

课程目标

探索数据可视化的基本原理,为继续深入学习打下基础,包括后续学习 Charlie Wu 等人在 Frontend Masters 上的 D3 课程。

1-data-visualization-use-cases

什么是数据可视化?

数据可视化的基本定义是将数据转换为图形表示的过程。但这个定义过于简单,没有体现数据可视化的真正价值。

为什么数据可视化重要?

对开发者的价值

  1. 工作需求:前端开发者经常需要在构建的网站中集成可视化功能
    • 内部工具的仪表板
    • 为用户展示数据洞察的应用程序
  2. 简历亮点:数据可视化技能在求职中很受欢迎
    • 可能是专门的数据可视化开发职位
    • 在前端或全栈开发中经常遇到相关需求

人类视觉感知优势

人类具有强大的视觉感知能力,通过图形化表示可以:

  • 更快速地发现数据中的意义和模式
  • 比查看原始数据行更准确地获得洞察
  • 为用户提供快速理解数据的方式

实际案例分析

原始数据 vs 可视化数据

课程展示了 GitHub API 测试数据的对比:

原始数据观察

  • 包含 GitHub 相关 URL
  • 涉及 Svelte.js 项目
  • 状态显示为"completed"和"success"
  • 难以快速发现模式

可视化后的发现

  • 显示测试频率和时间模式
  • 清晰展示测试持续时间(2-6 分钟)
  • 通过颜色区分成功/失败/取消状态
  • 大部分测试成功(绿色),少数失败(红色)

开发者的应用场景

功能开发

  • 需求分析:了解用户如何使用网站和产品
  • 优先级设定:分析用户设备类型来优化设计
  • 成功评估:追踪新功能的使用情况
  • 迭代指导:基于数据决定功能改进方向

性能监控

  • 系统速度:监控测试任务和 API 响应时间
  • 瓶颈识别:发现性能问题所在
  • 改进验证:证明性能优化的效果
  • 可靠性:监控错误率和系统稳定性

代码和开发流程分析

  • 代码库理解:分析目录结构和变化频率
  • 工作流优化:改善 CI/CD 流程
  • 团队协作:监控 PR 审核时间和响应速度
  • 开源项目:了解贡献者社区活动

通过数据可视化,开发者可以更好地理解系统性能、用户行为和团队协作效率,从而做出更明智的技术决策。

2-data-visualization-q-a

颜色使用的最佳实践

无障碍设计考虑

课程中提到的第一个问题涉及颜色无障碍设计,特别是红绿色盲友好性:

问题:如何构建对色盲友好的可视化? 解决方案

  • 使用适合不同视觉感知系统的颜色调色板
  • 在第一个实践项目中学习自定义颜色值
  • 考虑品牌色彩要求
  • 使用 Plot 库的灵活颜色定制功能

实际应用案例分享

医疗保健领域

  • 生命体征数据可视化
  • 性能监控理念与健康数据分析的相似性
  • 疫情期间图表使用量激增的现象

企业应用

  • Frontend Masters 的指标仪表板
  • 使用数据和收入可视化
  • Apple Watch 等设备的持续数据展示

零售业应用

  • 产品销售数据分析
  • 库存"神秘消失"现象追踪
  • 复杂业务流程的可视化需求

社会发展数据

  • 人类进步的数据驱动分析
  • Our World in Data 网站的案例
  • 气候变化等重要议题的数据呈现

法律领域应用

  • 软件开发背景律师的数据可视化需求
  • 案例处理流程的潜在分析机会

课程目标和方法

基础构建块方法

  • 专注于核心概念而非特定图表类型
  • 学习将数据特征映射到视觉特征的基本原理
  • 构建定制化可视化的能力

灵活性和定制化

  • 不局限于开箱即用的图表
  • 根据具体问题构建合适的可视化
  • 为不同领域和用例提供适应性

渐进学习路径

  • 从基础概念开始
  • 为后续深入学习 D3 等高级工具做准备
  • 通过 Observable Plot 学习高级抽象概念

这种教学方法确保学习者获得扎实的理论基础,能够应对各种实际应用场景,而不仅仅是记住特定的图表制作方法。

3-working-with-data

Observable 平台优势

为什么选择 Observable?

作为忙碌的开发者,我们希望快速上手数据可视化而不需要第二职业。Observable 正是为此设计:

核心特点

  • 浏览器内的响应式编程环境
  • 专为数据可视化设计
  • 由 D3 创建者 Mike Bostock 开发
  • 原名 D3 Express 项目

工作方式

  • 代码单元格(Code cells)编写 JavaScript
  • 使用声明式可视化库 Plot
  • 实时显示代码执行结果
  • 内置交互式输入组件

选择理由

  1. 基于 JavaScript,学习曲线平缓
  2. 完全在浏览器内运行,无需安装配置
  3. 响应式即时反馈,便于迭代调整
  4. 函数式响应编程基础

数据获取方法

数据源类型

在开始可视化之前,首先需要获得数据:

  1. 本地文件上传
    • JSON 或 CSV 文件
    • 小型数据集的快速开始方式
    • 可上传自定义数据进行个性化分析
  2. 云端文件连接
    • Google Spreadsheets
    • Microsoft OneDrive
    • 团队协作的便捷选择
  3. 数据库直连
    • MySQL、Postgres 等关系型数据库
    • 适用于大型数据集
    • 可集成到应用程序中
  4. API 数据获取
    • 实时数据源
    • GitHub API 等外部服务
    • 内部 API 接口

数据整理(Data Wrangling)

核心概念

数据整理是将原始数据转换为适合可视化格式的过程,是有效可视化的关键前提。

目标:将数据调整为回答特定问题所需的格式和结构。

JavaScript 数组方法

Filter-Map-Reduce 三明治模型

  1. Filter(过滤)

    // 类比:去除不喜欢的蔬菜(洋葱、黄瓜)
    // 实际:筛选特定设备类型(如只看移动端数据)
    data.filter((item) => item.deviceType === "mobile");
    
  2. Map(映射)

    // 类比:将整个蔬菜切片
    // 实际:转换数据格式或提取特定字段
    data.map((item) => ({ ...item, processedValue: transform(item.value) }));
    
  3. Reduce(聚合)

    // 类比:将切片蔬菜组合成三明治
    // 实际:计算总和、平均值等聚合操作
    data.reduce((acc, item) => acc + item.value, 0);
    

整洁数据(Tidy Data)

整洁数据原则

  • 一行一观察:每行代表一个独立观察结果
  • 一列一特征:每列代表观察对象的一个属性
  • 一格一值:每个数据值占据独立单元格

数据重塑示例

非整洁格式

Country    Year  Type        Count
Afghanistan 1999  cases      745
Afghanistan 1999  population 19987071
Afghanistan 2000  cases      2666
Afghanistan 2000  population 20595360

整洁格式

Country    Year  Cases  Population
Afghanistan 1999  745    19987071
Afghanistan 2000  2666   20595360

可视化映射过程

核心概念:尺度(Scales)

可视化的本质是通过尺度函数将数据空间的数值映射到视觉空间的元素:

数据空间(域 Domain)→ 尺度函数视觉空间(范围 Range)

示例

  • 数据值:0-10,000 → 像素宽度:0-500px
  • 类别值:成功/失败 → 颜色:绿色/红色

关键要点

可视化的核心是理解如何将数据特征通过尺度函数映射为视觉特征。这是整个数据可视化过程中最重要的概念。

4-javascript-tools-for-data-vis

JavaScript 数据可视化生态系统

主要工具库概览

D3.js

  • 低级别可视化库,功能强大
  • 需要深入了解尺度、组件等底层概念
  • 适合构建高度定制的可视化
  • 在 Frontend Masters 有 Shirley Wu 等讲师的深入课程

Observable Plot

  • 今天课程的主要工具
  • 高级抽象层,基于语法图形学
  • 与 D3 概念相似但更易上手
  • 为后续学习 D3 打下良好基础

Vega-Lite

  • 与 Plot 概念相似的高级库
  • 同样基于语法图形学原理
  • 提供声明式可视化方法

数据处理工具

Arquero

  • 华盛顿大学团队开发
  • 高级数据整理功能
  • 适合复杂数据变换需求

tidy.js

  • 专注于整洁数据处理
  • 与 R 语言 tidyverse 包兼容
  • 适合有 R 语言背景的开发者

开箱即用 vs 定制化

开箱即用图表库的局限性

  • 适合标准化需求
  • 在特定参数范围内有效
  • 缺乏灵活性和定制性

今天课程的重点

通用原理掌握

  • 理解数据到视觉的映射规律
  • 学习尺度编码的核心概念
  • 不受预设图表类型限制

灵活控制能力

  • 构建最适合特定用例的可视化
  • 避免被固定解决方案束缚
  • 保持创造性和适应性

学习策略平衡

课程在以下两个方面找到平衡:

  1. 定制化可视化:满足特殊需求的能力
  2. 高级抽象:快速构建的效率

这种方法确保学习者既掌握核心原理,又能高效实现目标,为日后根据实际需求选择合适工具打下基础。

5-grammar-of-graphics

Observable Plot 简介

基本信息

  • 开发团队:Observable 和 D3 的创建者
  • 设计目标:作为 D3 的高级抽象补充
  • 获取方式:observablehq.com/@observablehq/plot
  • 开源特性:GitHub 上可查看源代码,可贡献和提交问题

语法图形学(Grammar of Graphics)

核心理念

语法图形学是理解数据可视化抽象的系统性方法,将复杂的可视化任务分解为可组合的核心概念。

四个核心概念

1. 标记(Marks)

定义:屏幕上绘制的不同类型图形元素

类型示例

  • 刻度线(ticks)- 如测试任务可视化中的时间点
  • 条形/矩形(bars/rectangles)- 条形图的基本元素
  • 点/符号(dots/symbols)- 散点图中的数据点
  • 其他标记(x 符号等)

组合特性

  • 可叠加多种标记类型
  • 不同标记表达数据集的不同特征
  • 通过组合传达复杂含义

2. 尺度(Scales)

功能:实现抽象数据空间到具体图形空间的映射转换

重要性:这是我们之前讨论的核心概念,将数值、类别等数据特征转换为像素、颜色等视觉特征。

3. 变换(Transforms)

用途:类似数据整理中的处理操作

功能示例

  • 移动窗口平均值计算
  • 数据集数值求和
  • 数据分组聚合
  • 连续数据分箱处理

集成特性:许多变换功能内置在 Plot 中,减少预处理工作量。

4. 分面(Facets)

概念:也称为小倍数(Small Multiples)

应用场景

  • 为数据的特定子集创建小型可视化
  • 便于比较不同组别或类别
  • 在同一视图中展示多个相关图表

优势:有效处理复杂多维数据的展示需求。

组合式设计哲学

核心价值

通过这些概念化构建块的组合,可以:

  • 构建定制化可视化方案
  • 避免受限于预设图表类型
  • 灵活应对不同数据和问题需求
  • 保持代码的模块化和可重用性

实践方向

后续的实践练习将深入探索这四个核心概念的具体应用,通过动手操作理解语法图形学的实际价值。

部署和集成

跨平台使用

Plot 作为 JavaScript 库,可以在任何支持 JavaScript 的环境中使用:

  • Observable 平台(今天的学习环境)
  • 原生 JavaScript 项目
  • React 框架项目
  • 其他前端框架(遵循相同原理)

这种灵活性确保学习内容可以直接应用到实际开发工作中。

6-setup-project-data-in-plot-exercise

Observable 环境介绍

第一个实践项目:测试是否通过?

这是课程的第一个动手练习,学员将在 Interactive Observable 环境中工作。Observable notebook 结合了代码、文本、标题和可视化功能。

项目背景:GitHub Actions 数据

数据来源

  • GitHub Actions API 的测试作业数据
  • 专注于持续集成(CI)中的自动化测试任务
  • 使用开源 SvelteJS 仓库的公开数据作为示例

数据预处理

  • 已附加 JSON 文件到 notebook
  • 预先完成数据清理和过滤
  • 移除不相关数据,优化可视化准备

Observable 编辑操作

基本操作流程

  1. 在灰色或蓝色代码框中编辑 JavaScript 代码
  2. 编辑后出现蓝色播放按钮
  3. 点击播放按钮或按 Shift+Enter 执行代码
  4. 实时查看结果更新

数据探索

// 解析JSON文件
testJobs = FileAttachment("data.json").json();

// 表格形式浏览数据
testJobs[0]; // 查看第一个元素

Plot 可视化基础

第一个简单可视化

基本结构

Plot.plot({
  marks: [
    Plot.cell(testJobs, {
      x: "runNumber",
      y: "name"
    })
  ],
  width,
  margin: {...},
  x: { tickRotate: 45 }
})

可视化解读

  • 使用 cell 标记创建类似表格单元格的矩形
  • 每行数据对应一个单元格
  • x 轴:运行编号(run number)
  • y 轴:测试作业名称(name)

通道(Channels)概念

核心通道类型

  • x: 水平位置
  • y: 垂直位置
  • fill: 内部填充颜色
  • fillOpacity: 填充透明度
  • stroke: 轮廓/边框颜色
  • strokeOpacity: 轮廓透明度
  • title: 工具提示文本

第一个练习:使用颜色编码

目标:使用 fill 通道可视化测试结论状态

  • 成功(success)
  • 失败(failure)
  • 取消(canceled)

实现方式

Plot.cell(testJobs, {
  x: "runNumber",
  y: "name",
  fill: "conclusion", // 添加这一行
});

关键技巧

  • 记住在 JSON 对象中添加逗号
  • 使用数据集中的"conclusion"列
  • 实时查看颜色变化效果

通过这个基础练习,学员开始理解通道映射的概念,为后续更复杂的可视化定制奠定基础。

7-setup-project-data-in-plot-solution

第一个练习解答:添加颜色编码

解决方案实现

代码解答

Plot.cell(testJobs, {
  x: "runNumber",
  y: "name",
  fill: "conclusion", // 关键添加:映射结论状态到填充颜色
});

重要提醒

  • 最难的部分是记住添加逗号
  • 语法模式与 x、y 映射完全相同
  • 按 Shift+Enter 执行代码

结果观察

  • 不再是单调的黑色方块
  • 通过颜色区分不同测试状态
  • 可视化信息量显著提升

尺度(Scales)深入理解

尺度的工作原理

映射过程

输入域(Domain) → 尺度函数 → 输出范围(Range)
数据值空间 → 转换处理 → 视觉特征空间

默认行为分析

  • Plot 自动检测数据中的唯一值(success, failure, canceled)
  • 创建 3 个值的域
  • 从默认颜色范围选取前 3 个颜色

颜色无障碍设计考虑

当前问题

  • 红、蓝、黄色彩无明确语义
  • 对色盲用户不够友好
  • 缺乏直观的状态表达

改进机会

  • 选择无障碍色彩搭配
  • 使用语义化颜色(绿色=成功)
  • 参考无障碍设计资源

自定义颜色尺度

color 尺度配置

尺度范围

  • color 尺度同时控制 fill 和 stroke 通道
  • 当前只使用 fill 通道
  • 需要通过 color 属性自定义

下一步预告: 接下来将学习如何通过 domain 和 range 属性完全控制颜色映射,确保颜色选择与数据语义匹配。

这个练习展示了从默认可视化到有意义可视化的第一步转换,为理解更复杂的尺度定制打下基础。

8-setting-domain-range-colors-exercise

尺度的域和范围配置

当前状态分析

数据域识别: 通过检查数据表格,发现 conclusion 字段包含三个值:

  • success(成功)
  • failure(失败)
  • canceled(取消)

域排序策略: 由于这些是分类值而非数值,没有自然顺序,需要人为选择排列:

domain: ["success", "failure", "canceled"];

配置语法结构

基本配置模式

color: {
  domain: [array_of_input_values],
  range: [array_of_output_values]
}

实际配置示例

域设置

domain: ["success", "failure", "canceled"];
// success - 理想状态,排在第一位
// failure - 问题状态,排在第二位
// canceled - 异常状态,排在最后

测试域排序: 执行 Shift+Enter 后观察颜色变化,确认 Plot 的默认域顺序与我们的设定是否一致。

范围设置

range: ["green", "red", "gray"];
// green - 对应success,表示成功
// red - 对应failure,表示失败
// gray - 对应canceled,淡化异常状态

颜色选择考虑

传统但不完全无障碍的选择

  • 绿色:文化上表示成功、通过
  • 红色:文化上表示失败、错误
  • 灰色:中性色,表示取消或无关紧要

改进建议

  • 鼓励学员选择无障碍色彩方案
  • 可以使用黄色代替灰色
  • 完全自定义其他颜色组合

灵活性展示

range: ["green", "red", "yellow"]; // 或任何其他颜色组合

尺度概念泛化

通用原理: 这种域和范围的配置方法适用于 Plot 中的所有尺度类型,不仅仅是颜色尺度。学员可以对 x 尺度、y 尺度等进行类似的定制化配置。

下一步预告: 下个练习将添加新的数据特征可视化,探索如何在单一图表中编码多个数据维度。

9-setting-domain-range-colors-solution

多通道编码练习解答

添加时长可视化

第一部分:fillOpacity 通道

fillOpacity: "minutes";

效果分析

  • 测试运行时间越长,单元格越深色
  • 长时间运行的测试更容易被注意到
  • 符合性能瓶颈识别需求

视觉设计逻辑

  • 深色 = 高显著性 = 需要关注的长耗时测试
  • 可根据需求反向映射(让短时间测试突出)

处理零时长问题

问题识别

  • 部分测试显示 0 分钟时长
  • 0 透明度导致完全不可见
  • 丢失状态信息(成功/失败/取消)

解决方案 A:常量描边

stroke: "black"; // 所有单元格使用黑色边框

解决方案 B:动态描边编码

stroke: "conclusion"; // 边框颜色编码状态信息

信息冗余的价值

双重编码策略

  • fill 通道:编码结论状态
  • stroke 通道:重复编码结论状态
  • fillOpacity 通道:编码时长信息

冗余的优势

  • 即使 fillOpacity 为 0,仍可通过 stroke 查看状态
  • 增强可读性和信息的可靠传达
  • 适应不同视觉感知需求

数据洞察发现

零时长测试分析: 通过 stroke 编码发现:

  • 一些零时长测试是取消状态(canceled)
  • 可能在实际执行前就被取消
  • 一些可能是立即错误退出

可视化设计决策

  • 常量值 vs 数据映射的选择
  • 根据具体分析需求决定编码策略
  • 平衡视觉清晰度和信息密度

这个练习展示了多维数据编码的强大功能,以及如何通过冗余编码增强可视化的鲁棒性。

10-adding-tooltips

交互性增强:自定义工具提示

title 通道与函数映射

基本语法结构

title: (d) => `${d.conclusion} after ${d.minutes.toFixed(2)} minutes`;

函数参数约定

  • 通常使用d表示数据点(datum)
  • 可自由命名:jobrowdataPoint
  • 函数接收数据集中的每个对象

字符串模板技术

模板字面量优势

// 推荐:使用模板字面量
title: (d) => `${d.conclusion} after ${d.minutes.toFixed(2)} minutes`;

// 替代方案:字符串拼接
title: (d) => d.conclusion + " after " + d.minutes.toFixed(2) + " minutes";

// 替代方案:数组连接
title: (d) =>
  [d.conclusion, "after", d.minutes.toFixed(2), "minutes"].join(" ");

数值格式化

  • toFixed(2):保留两位小数
  • 提升数据展示的专业性
  • 避免过长的小数显示

实现示例和效果

完整实现

Plot.cell(testJobs, {
  x: "runNumber",
  y: "name",
  fill: "conclusion",
  fillOpacity: "minutes",
  stroke: "conclusion",
  title: (d) => `${d.conclusion} after ${d.minutes.toFixed(2)} minutes`,
});

交互体验

  • 悬停显示详细信息
  • 显示格式:例如"cancelled after 3.10 minutes"
  • 增强数据探索能力

技术细节和故障排除

常见问题

  • 工具提示可能存在显示 bug
  • Observable Plot 作为开源项目,欢迎 bug 报告
  • GitHub 地址:github.com/observablehq/plot

自定义灵活性: 学员可以包含其他信息:

  • 测试名称
  • 运行编号
  • 个性化描述文本

可视化完整性回顾

多维信息编码总结

  1. 位置编码:x 轴(运行编号) + y 轴(测试名称)
  2. 颜色编码:fill(结论状态) + stroke(结论状态冗余)
  3. 透明度编码:fillOpacity(运行时长)
  4. 交互编码:title(详细信息工具提示)

设计价值

  • 单一图表回答多个业务问题
  • 冗余编码增强信息传达
  • 交互性提供按需详细信息
  • 高信息密度但保持可读性

交互限制和扩展路径

Plot 交互能力

  • 相对简单但高效的交互选项
  • 专注快速开发和高级抽象
  • 支持基本的鼠标悬停状态

高级交互需求

  • 复杂动画效果需要 D3.js
  • 精细化交互控制建议使用低级库
  • Frontend Masters 的 Shirley Wu 课程作为进阶路径

这个最终练习完成了从静态可视化到交互式数据探索工具的转化,展示了 Plot 在快速原型开发中的强大能力。

11-analytics-project-data-setup

第二个项目:用户设备尺寸分析

项目背景和数据源

新数据集介绍

  • 文件名:device analytics.csv
  • 数据类型:CSV(逗号分隔值)文件
  • 数据性质:模拟的网站访问分析数据
  • 数据来源:类似 Google Analytics 导出的数据格式

数据内容结构

  • device: 设备类型分类(desktop、mobile、tablet)
  • resolution: 屏幕分辨率字符串(如"1920x1080")
  • users: 该设备/分辨率组合的用户数量
  • sessions: 该设备/分辨率组合的会话数量

数据处理需求分析

当前数据格式问题

  1. 分辨率数据整合
    • 当前:单一字符串"1920x1080"
    • 需要:分离的 width(1920)和 height(1080)数值列
    • 目的:支持连续数据空间的数值处理
  2. 数据类型转换
    • 当前:users 和 sessions 为字符串类型
    • 需要:转换为数值类型
    • 目的:支持数学运算和聚合操作

数据整理任务

使用 Array.map()进行数据变换

目标转换

// 原始数据示例
{
  device: "desktop",
  resolution: "1920x1080",
  users: "1250",
  sessions: "2100"
}

// 目标数据格式
{
  device: "desktop",
  resolution: "1920x1080",
  width: 1920,     // 新增数值列
  height: 1080,    // 新增数值列
  users: 1250,     // 转换为数值
  sessions: 2100   // 转换为数值
}

实现策略提示

  • 使用split()方法分离分辨率字符串
  • 使用+操作符快速转换字符串为数值
  • 保持原始数据列的同时添加新处理列

数据整理的重要性

这个轻量级数据整理练习展示了:

  • 为后续可视化做准备的必要性
  • 数据格式对分析效率的影响
  • 真实世界数据工作中的常见转换需求

通过这个练习,学员将体验到良好的数据预处理如何为后续的复杂可视化分析奠定基础。

12-visualizing-analytics-data

数据整理解决方案

实现方法多样性

基本解决策略

devices = deviceAnalytics.map((d) => {
  const [width, height] = d.resolution.split("x");
  return {
    device: d.device,
    resolution: d.resolution,
    width: +width, // 字符串转数值
    height: +height, // 字符串转数值
    users: +d.users,
    sessions: +d.sessions,
  };
});

关键技术点

  • split("x"):按"x"分隔符拆分分辨率字符串
  • 解构赋值:[width, height] = 数组结果
  • +操作符:快速字符串到数值转换
  • 对象展开:保持原有属性同时添加新属性

数据质量问题处理

缺失数据处理策略

问题场景:部分记录显示"not set"分辨率值

处理选项

  1. 使用 null 值:为 width 和 height 设置 null
  2. 设置默认值:使用 0 或其他合理默认值
  3. 过滤排除:使用filter()移除缺失数据
  4. 分类处理:将缺失数据作为特殊类别处理

推荐方法

// 过滤掉缺失数据
.filter(d => d.resolution !== "not set")
// 或在可视化中单独处理零值

聚合可视化入门

Transform 概念介绍

语法图形学中的 Transform

  • 数据转换和聚合操作
  • 按分类变量分组数据
  • 计算统计量(求和、计数、比例等)

分类变量 vs 连续变量

  • 分类变量:设备类型(desktop、mobile、tablet)
  • 连续变量:用户数量(数值范围)

Plot 的 Group Transform

基本用法模式

Plot.barY(
  devices,
  Plot.groupX(
    { y: "count" }, // 输出:计算结果映射
    { x: "device" } // 输入:分组依据
  )
);

第一个聚合图表

频率计数条形图

代码结构分析

Plot.barY(
  devices,
  Plot.groupX(
    { y: "count" }, // 输出通道:计算每组观察数量
    { x: "device" } // 输入通道:按设备类型分组
  )
).plot({
  width,
  marginLeft: 100,
});

语法变体说明

  • 方法 1:Plot.plot({marks: [Plot.barY(...)]})
  • 方法 2:Plot.barY(...).plot({...}) (单标记时的简化写法)

可视化解读

  • desktop:最高频率(最多观察记录)
  • mobile:中等频率
  • tablet:最低频率

Transform 函数工作机制

输入输出关系

  • 输入(第二参数):分组依据 {x: "device"}
  • 输出(第一参数):聚合结果 {y: "count"}
  • 轴向对应:groupX 与 barY 配合(垂直轴分组)

聚合函数选项

  • count:计数观察次数
  • sum:数值求和
  • mean:平均值
  • proportion:比例计算

这个基础聚合图表为理解更复杂的数据分组和统计计算奠定了基础,展示了如何从原始数据快速获得高层次的业务洞察。

13-aggregation-grouping-exercise

从观察计数到用户总数

业务价值导向的调整

当前图表局限性

  • 显示数据集中的观察记录频次
  • 不反映实际业务价值(用户数量)
  • 需要转换为更有意义的指标

目标转换

  • 从"每个设备类型有多少条记录"
  • 转为"每个设备类型有多少用户"

练习任务分解

主要任务:用户数求和

将 count 聚合改为 sum 聚合,计算每个设备类型的总用户数

实现思路

// 从这个开始:
Plot.groupX({ y: "count" }, { x: "device" });

// 改为:
Plot.groupX({ y: "sum" }, { x: "device", y: "users" });

关键概念

  • 需要指定要求和的数据列(users)
  • 输入配置中添加 y 通道映射
  • 聚合函数从 count 改为 sum

扩展任务:视觉区分

颜色编码增强: 为三个设备类型条形图添加不同颜色,增强视觉区分度

实现提示

  • 使用 fill 通道
  • 映射到 device 分类变量
  • 利用 Plot 默认颜色方案

技术挑战点

Transform 理解难点

  • 输入输出参数的对应关系
  • 同时配置分组依据和聚合目标
  • 通道映射在 transform 中的作用机制

学习建议

  • 从简单的 count 开始理解
  • 逐步添加复杂聚合操作
  • 对比输入数据和输出结果

实践价值

这个练习展示了数据可视化中常见的需求转换:

  • 从技术指标到业务指标
  • 从频次统计到数值聚合
  • 从单维展示到多维编码

通过 hints 按钮获得具体实现指导,重点掌握 group transform 的输入输出配置模式。

14-aggregation-grouping-solution

聚合变换解决方案详解

数据处理复杂性认知

学习难度正常化

  • 这个练习确实比之前的更具挑战性
  • 困难体现了 Transform 概念的复杂程度
  • 证明了数据预处理阶段处理聚合的价值

缺失数据处理策略

"not set"分辨率值处理

问题分析:数据集中存在分辨率值为"not set"的记录

处理选项

  1. null 值策略:width 和 height 设为 null
  2. 默认值策略:使用 0 或其他合理默认值
  3. 过滤策略:使用 filter()排除缺失数据行
  4. 分类策略:作为特殊类别单独处理

实际影响:在后续分辨率可视化中,可能出现(0,0)坐标点,代表缺失数据的转换结果。

用户总数聚合实现

核心实现逻辑

Plot.barY(devices, Plot.groupX(
  {y: "sum"},                    // 输出:sum聚合而非count
  {x: "device", y: "users"}      // 输入:添加y通道指定聚合目标
)).plot({...})

关键理解

  • 单纯改 count 为 sum 不足以完成任务
  • 必须在输入配置中指定 y: "users"
  • 告诉 Plot 对 users 列进行分组求和

数据流程

  1. 按 device 分组数据
  2. 对每组的 users 值求和
  3. 将求和结果映射到 y 轴高度

视觉增强功能

颜色区分

{x: "device", y: "users", fill: "device"}

设计决策

  • fill 通道映射 device 分类
  • 不需要聚合,直接使用原值
  • 默认颜色方案足以区分三个类别

工具提示增强

{
  x: "device",
  y: "users",
  fill: "device",
  title: "users"  // 添加工具提示
}

实现机制

  • title 通道同样使用 sum 聚合
  • 与 y 通道计算相同的数值
  • 提供悬停时的精确数值显示

可视化结果洞察

数据发现

  • 桌面用户数量远超平板用户
  • 移动端用户数量居中
  • 数量级差异更加显著(相比观察计数)

冗余编码价值

  • y 轴:通过条形高度显示总用户数
  • title:通过工具提示显示相同数值
  • 类似前项目中的 fill/stroke 双重编码策略

扩展练习建议

会话数据分析:鼓励学员独立实现按 sessions 字段的聚合分析,应用相同的 transform 模式。

这个解答展示了数据聚合的核心概念,以及如何通过适当的输入输出配置实现复杂的统计可视化。

15-scatterplot-exercise

从聚合到详细分析

聚合的双刃性

聚合的价值

  • 回答宏观问题:"移动端用户多还是桌面端用户多?"
  • 提供清晰的大局观
  • 简化复杂数据的理解

聚合的局限性

  • 隐藏细节信息和微妙模式
  • 掩盖数据的精细分布特征
  • 可能遗漏重要的异常情况

详细分析的业务价值

设计优化需求

屏幕分辨率分析

  • 最常见宽度:确保核心设计在主流宽度下表现完美
  • 主流纵横比:优化布局适配性
  • 跨设备模式:发现用户在不同设备上的使用习惯重叠

性能优化洞察

异常使用模式发现

  • 桌面端窄屏幕使用:可能表示侧边栏浏览习惯
  • 设备分类边界模糊:平板/手机界限的真实使用情况
  • 高分辨率设备:识别需要特殊优化的高端设备

连续数据的处理方法

Bin Transform 简介

与 Group Transform 的对比

  • Group:处理分类变量(desktop、mobile、tablet)
  • Bin:处理连续变量(数值范围分段)

Bin 使用场景

// 将分辨率宽度分组到100px区间
Plot.binX({ y: "count" }, { x: "width", thresholds: 100 });

散点图设计任务

预配置画布特性

基础设置解析

Plot.plot({
  width,
  height: width * 0.5, // 2:1宽高比
  x: { grid: true, domain: [0, 4200] }, // x轴网格和域限制
  y: { grid: true, domain: [0, 2400] }, // y轴网格和域限制
  opacity: { range: [0.1, 1] }, // 透明度范围配置
});

设计决策说明

  • 域限制:聚焦主流分辨率范围,排除极端值
  • 网格系统:便于读取坐标值
  • 透明度预设:为后续重叠处理做准备

任务分解

核心映射

Plot.dot(devices, {
  x: "width", // 水平轴:屏幕宽度
  y: "height", // 垂直轴:屏幕高度
});

增强编码

  1. 设备类型区分:使用颜色或形状编码 device
  2. 会话数量展示:通过点的大小或透明度显示 sessions
  3. 交互详情:添加工具提示显示完整信息

分析期待

模式发现预期

  • 常见分辨率的聚类分布
  • 设备类型与分辨率的对应关系
  • 高频使用分辨率的识别
  • 异常或边缘使用情况

通过散点图,学员将体验从聚合视角转向详细探索的分析方法转换,理解不同可视化方法对应不同分析需求的重要性。

16-scatterplot-solution

散点图基础设置

创建基本散点图

  • plot.dot的选项中设置基本映射:
    • x: "width" - 将宽度映射到 x 轴
    • y: "height" - 将高度映射到 y 轴
  • 通过这种映射,可以在图表上看到数据点的分布

观察数据模式

散点图中出现的对角线模式反映了:

  • 宽度和高度之间存在恒定比例关系
  • 这些对角线代表特定的宽高比(aspect ratios)
  • 随着宽度增加,高度按比例缩放,显示数据中存在常见的屏幕比例

添加颜色编码

使用设备类别进行颜色映射

  • 添加 fill: "device" 来区分不同设备类型
  • 观察结果:
    • 橙色小点:移动设备
    • 蓝色大点:桌面设备
    • 红色中等点:平板设备

处理点重叠问题

  • 使用 fill-opacity: 0.5 设置透明度
  • 或者将不透明度映射到会话数据:fill-opacity: "sessions"
  • 较暗的点表示该分辨率和设备类别组合有更多会话

点大小映射

使用半径通道

  • 添加 r: "sessions" 将点大小映射到会话数
  • 问题:会话数差异过大,导致可视化效果不佳

数据变换解决方案

对数变换方法:

  1. 在通道内使用函数变换:r: d => Math.log(d.sessions)
  2. 在缩放设置中指定对数类型:opacity: {type: "log"}

变换的重要性:

  • 没有对数缩放时,高数值会掩盖低数值的细节
  • 对数变换帮助平衡不同数量级的数据
  • 确保可视化不会无意中隐藏重要信息

数据洞察

宽高比模式分析

  • 对角线模式:常见的屏幕宽高比
  • 垂直线模式:用户固定浏览器宽度,但改变高度
  • 桌面设备:多为横向(宽>高)
  • 移动设备:多为纵向(高>宽)

用户行为推测

  • 桌面用户可能将浏览器设置为半屏或默认宽度
  • 通过滚动或拖拽改变窗口高度
  • 移动设备主要以竖屏模式使用

技术要点

缩放配置

  • 合理的缩放类型选择对数据可视化至关重要
  • 避免使用默认缩放掩盖数据细节
  • 根据数据分布特点选择线性或对数缩放

多通道映射

同时使用多个视觉通道可以编码更多信息:

  • 位置(x, y)
  • 颜色(fill)
  • 透明度(fill-opacity)
  • 大小(r)
  • 工具提示(title)

17-facets-for-comparing-group-data-exercise

分面图概念介绍

分面图的作用

  • 解决重叠问题:当所有数据点聚集在一个图表中时,不同组的数据会相互遮挡
  • 揭示隐藏细节:分离不同组别后,可以发现被主要数据掩盖的模式
  • 便于比较:将数据按类别分组展示,便于对比不同子集的特征

小多重图概念

  • 分面图也称为"小多重图"(small multiples)
  • 每个小图表可视化数据的一个子集
  • 将多个小图表组合在一起,可以比较数据集的不同子集

分面图的应用场景

数据集自然分组

当前数据集天然地分为三个子集:

  • 桌面设备
  • 移动设备
  • 平板设备

观察需求

  • 发现混合在一起时难以察觉的模式
  • 对比不同设备类别的使用特征
  • 突出较小子集中的重要信息

分面语法结构

基本语法

plot() 调用中添加 facet 属性:

facet: {
  data: 数据源,        // 通常与绘图使用的数据相同
  x: "分组字段名"      // 或 y: "分组字段名"
}

通道选择

  • x 通道:水平排列的分面
  • y 通道:垂直排列的分面
  • 根据数据特点和展示空间选择合适的排列方式

实践任务

操作步骤

  1. 复制之前创建的散点图代码
  2. plot() 配置中添加 facet 属性
  3. 使用 device 字段作为分组依据
  4. 尝试不同的排列方向(x 或 y)

预期效果

  • 将单个复合散点图分解为三个独立的小图
  • 每个小图只显示一种设备类型的数据
  • 减少颜色编码的必要性,因为每个分面已经隐式表示设备类型

探索建议

  • 比较水平和垂直排列的视觉效果
  • 观察哪种排列方式更适合当前数据
  • 考虑输出格式和可用空间对选择的影响

18-facets-for-comparing-group-data-solution

分面图实现步骤

语法实现

在 plot 配置中添加 facet 属性:

facet: {
  data: devices,           // 使用相同的数据源
  x: "device"             // 按设备类型在x轴方向分面
}

语法位置说明

  • 使用 plot.mark.plot() 语法时:添加到 .plot() 的配置对象中
  • 使用 plot.plot() 语法时:添加到主配置对象中
  • 记住添加逗号分隔符

分面图效果分析

视觉改进

  • 从单个图变为三个并排图:Desktop、Mobile、Tablet
  • 减少颜色混淆:每个分面主要显示一种颜色
  • 突出隐藏数据:之前被桌面数据掩盖的移动和平板数据点现在清晰可见

新发现的模式

  • 移动设备的横向模式:在分面图中可以清楚看到红色点(平板)在横向(宽>高)位置
  • 数据分布差异:不同设备类型的数据密度和分布模式更加明显
  • 边界数据:一些超出坐标轴范围的重叠数据点也能被识别

排列方向选择

X 轴 vs Y 轴分面

// 水平排列
facet: { data: devices, x: "device" }

// 垂直排列
facet: { data: devices, y: "device" }

选择考虑因素

  • 可读性:根据数据特征选择更易理解的排列方式
  • 空间利用:考虑输出环境的宽高比例
  • 用户偏好:不同的人对排列方式有不同感受
  • 数据特性:某些数据在特定排列下表现更好

项目总结

完成的关键步骤

  1. 数据整理:使用 map 操作预处理数据,分离宽度和高度信息
  2. 聚合分析:通过 group transforms 进行分类变量的聚合操作
  3. 散点图创建:使用 dot 标记创建信息丰富的可视化
  4. 多通道映射:利用 fill、opacity、radius 等通道编码不同数据特征
  5. 分面展示:通过 faceting 揭示被掩盖的数据细节

技术要点回顾

  • 数据预处理 vs 图表内变换:两种方法都有效,根据个人喜好选择
  • 聚合的价值:group transforms 帮助理解分类数据的总体模式
  • 多通道优势:同时使用多个视觉通道可以传达更丰富的信息
  • 分面的重要性:小多重图是揭示子群体特征的强大工具

19-api-response-log-data-setup

项目背景介绍

数据来源和目标

  • 项目名称:API 响应速度分析(How fast are API responses?)
  • 数据类型:API 日志数据的结构化版本
  • 分析目标:理解不同 API 端点的响应性能特征

数据集结构

apiLogData.json文件中解析出的数据包含:

  • duration:响应时间(毫秒)
  • status:HTTP 状态码(不全是 200)
  • path:API 端点路径
  • timestamp:请求时间戳

数据预处理

时间戳转换

// 将字符串时间戳转换为Date对象
// 目的:在时间轴上正确绘制而非作为字符串处理

数据表格预览

通过inputs.table展示数据结构,便于理解:

  • 响应时间范围和分布
  • 状态码的多样性(包含错误状态)
  • API 端点的种类和频次
  • 请求的时间序列特征

散点图任务设计

基本要求

  • X 轴:timestamp(时间戳)- 提供请求的时序信息
  • Y 轴:duration(响应时间,毫秒)- 性能指标
  • 数据点:每个 API 请求作为一个点

时间轴的作用

  • 不关心具体的请求时间
  • 提供数据的顺序排列方式
  • 帮助识别时间相关的性能趋势

增强可视化建议

路径区分

  • 颜色编码:使用不同颜色表示不同的 API 端点
  • 流量分析:识别高频访问的端点
  • 性能对比:比较不同端点的响应时间特征

视觉优化

  • 透明度调整:处理数据点重叠问题
  • 工具提示:显示详细的请求信息
  • 日期标签:可以保留或隐藏 X 轴的日期显示

交互性预告

项目重点

  • 本项目的核心是添加交互控件
  • 超越基本的工具提示功能
  • 实现用户可控制的图表过滤和显示选项

技能发展

  • 巩固之前学习的绘图技能
  • 减少指导,增强独立操作能力
  • 为后续交互功能开发奠定基础

实用价值

  • 模拟真实的 API 性能监控场景
  • 学习如何从日志数据中提取可视化洞察
  • 理解性能数据的多维度分析方法

20-adding-ui-with-input-widgets

基础散点图回顾

参考解决方案特征

  • X 轴:timestamp(时间戳),提供请求序列
  • Y 轴:duration(响应时间),越高表示性能越差
  • 异常值识别:30 秒的极长响应时间
  • 性能分布:大量快速响应 + 逐渐减少的中等响应时间

颜色编码洞察

  • 路径映射:使用 fill 通道按 API 路径着色
  • 主要发现files/search路径占据大量请求
  • 性能问题:该路径也是慢响应的主要来源
  • 其他问题路径user/files等也显示性能问题

工具提示增强

title: (d) => `路径: ${d.path}\n响应时间: ${d.duration}ms\n状态: ${d.status}`;

Observable 输入控件系统

Inputs 对象介绍

Observable 提供预制的 HTML 控件集合:

  • inputs.table:数据表格显示
  • inputs.checkbox:多选框控件
  • 其他控件:通过侧边栏 plot 图标可见完整列表

复选框示例

viewof favColors = Inputs.checkbox(
  ["red", "orange", "yellow", "green", "blue", "indigo", "violet"],
  {label: "Colors I like"}
)

响应式编程概念

viewof 操作符

  • 特殊语法viewof variableName = Inputs.控件类型(...)
  • 功能:创建响应式的用户输入控件
  • 结果variableName变成当前选中值的实时引用

响应式更新机制

  • 自动响应:使用该变量的所有单元格会自动更新
  • 实时同步:用户交互立即反映到依赖单元格
  • 示例效果:选中不同颜色,相关文本自动更新

实际应用示例

// 控件定义
viewof selectedColors = Inputs.checkbox([...colors])

// 使用变量的单元格会自动响应变化
selectedColors.map(color => color.toUpperCase())
"My favorite colors are: " + selectedColors.join(", ")

图表交互化策略

连接输入控件与图表

  • 核心思想:将输入控件的值用于控制图表显示的数据
  • 响应式重绘:用户改变选择时图表自动更新
  • 数据过滤:基于用户选择决定可视化哪些数据子集

Observable 环境优势

  • 内置响应性:无需手动管理状态更新
  • 即插即用:输入控件与图表无缝集成
  • 实时反馈:用户操作的即时视觉响应

下一步实现

将要实现:用户通过交互控件选择要显示的数据维度,图表根据选择动态更新显示内容。

21-filtering-the-data-exercise

任务目标

创建状态码过滤器

  • 为数据集中的不同 HTTP 状态码创建复选框输入控件
  • 将复选框与图表连接,实现数据过滤功能
  • 只显示用户选择的状态码对应的响应数据

基础设置

  • 已提供 statusCodes 数组:包含数据中所有观察到的状态码
  • 需要创建 selectedCodes 变量来捕获用户选择
  • 使用之前的颜色复选框示例作为参考

复选框控件结构

基本语法框架

viewof selectedCodes = Inputs.checkbox(
  // 选项数组
  [],
  // 配置选项
  {
    label: "状态码标签"
  }
)

实现要点

  • 使用提供的 statusCodes 数组作为选项值
  • 添加合适的标签(如"Status Code")
  • 确保变量名为 selectedCodes 以便后续使用

设计考虑

用户体验

  • 为不熟悉 HTTP 状态码的用户提供直观的选择界面
  • 允许用户专注于特定类型的响应(如错误或成功)
  • 支持多选以便比较不同状态码的模式

数据探索价值

  • 分离正常响应(200)和错误响应(4xx, 5xx)
  • 识别特定状态码的性能特征
  • 揭示不同错误类型的分布模式

22-filtering-the-data-solution

复选框实现详解

基本结构实现

viewof selectedCodes = Inputs.checkbox(
  statusCodes,  // 使用预定义的状态码数组
  {
    label: "Status Code"  // 添加描述性标签
  }
)

默认全选配置

viewof selectedCodes = Inputs.checkbox(
  statusCodes,
  {
    label: "Status Code",
    value: statusCodes  // 设置默认选中所有选项
  }
)

Observable 响应式特性

默认值的重要性

  • 用户友好:避免空白图表的困惑体验
  • 完整视图:从全数据视角开始探索
  • 渐进过滤:用户可以逐步移除不感兴趣的类别

响应式数据流

  • selectedCodes 变量实时反映用户选择
  • 取消选择时,对应值从数组中移除
  • 所有依赖此变量的单元格自动更新

Observable 界面操作

单元格重排功能

  • 拖拽操作:点击单元格旁的三点图标进行拖拽
  • 位置灵活性:单元格顺序不影响执行逻辑
  • 依赖关系:响应式运行时根据变量依赖确定执行顺序
  • 布局优化:将相关控件放置在接近的位置提升用户体验

实用界面管理

  • 将复选框控件移至绘图代码附近
  • 删除不必要的多余单元格
  • 保持笔记本的整洁和逻辑性

交互设计原则

控件位置策略

  • 就近原则:控件与受影响的图表相邻放置
  • 逻辑分组:相关功能的控件集中管理
  • 视觉关联:用户容易理解控件与图表的关系

23-plotting-the-filtered-data-exercise

交互式数据过滤概念

用户驱动的数据探索

  • 决策权转移:从开发者预设转向用户自主选择
  • 灵活性价值:用户可以根据兴趣专注特定数据子集
  • 仪表板思维:为他人构建工具时的重要考虑

过滤的实际应用场景

  • 专注于错误响应分析(4xx, 5xx 状态码)
  • 排除正常响应查看异常模式
  • 比较不同状态码的性能表现

技术实现方法

数据过滤集成

// 基本思路:在绘图前过滤数据
apiLogData.filter(/* 根据selectedCodes过滤 */);
// 然后传入plot配置

Array.filter()方法应用

  • 使用 JavaScript 原生的 .filter() 方法
  • 结合 selectedCodes 数组进行条件判断
  • 只保留状态码匹配的数据点

实现任务要点

步骤概述

  1. 复制基础图表:从前面的散点图解决方案开始
  2. 添加过滤逻辑:在数据传入 plot 之前进行过滤
  3. 连接控件:使用 selectedCodes 变量作为过滤条件
  4. 测试交互:验证复选框变化能正确更新图表

技术提示

  • 可以使用数组的 .includes() 方法检查状态码是否在选中列表中
  • 考虑多种实现方式,选择最清晰易懂的方案
  • 确保过滤逻辑正确处理用户的动态选择

预期效果

  • 取消选择某些状态码时,对应数据点从图表中消失
  • 图表颜色分布可能发生变化(因为某些路径主要返回特定状态码)
  • 用户可以专注于特定类型的 API 响应进行分析

24-plotting-the-filtered-data-solution

过滤实现方案

核心过滤逻辑

apiLogData.filter((d) => selectedCodes.includes(d.status));

工作原理:

  • 遍历每个数据点(datum)
  • 检查该点的状态码是否在 selectedCodes 数组中
  • includes() 返回 true 则保留,false 则过滤掉

完整实现示例

plot
  .dot(
    apiLogData.filter((d) => selectedCodes.includes(d.status)),
    {
      x: "timestamp",
      y: "duration",
      fill: "path",
      // 其他配置...
    }
  )
  .plot({
    // plot配置...
  });

交互效果观察

动态数据变化

  • 数据点减少:取消选择状态码时,对应点从图表消失
  • 颜色分布变化:某些路径主要返回特定状态码,过滤后颜色模式改变
  • 完整恢复:重新选中所有选项可回到原始视图

实际分析价值

专注于服务器错误(503)时的发现:

  • 快速识别问题响应
  • 所有 503 错误都来自搜索端点
  • 响应时间达到 30 秒的极端情况

坐标轴动态调整

自适应缩放行为

Plot 自动根据当前数据范围调整坐标轴:

  • 优点:最大化利用可视化空间
  • 缺点:数据变化时轴范围跳动,影响比较

解决方案:固定轴范围

// 可以通过domain配置固定轴范围
y: {
  domain: [0, maxDuration]; // 固定Y轴范围
}

何时使用固定轴:

  • 需要保持比较基准一致
  • 避免视觉上的跳跃干扰
  • 强调数据的绝对而非相对关系

交互式可视化设计原则

用户控制平衡

  • 适度自动化:Plot 的智能缩放通常有帮助
  • 用户期望:某些情况下用户希望保持一致的比较基准
  • 场景判断:根据分析目标决定是否需要干预默认行为

渐进式披露

  • 默认显示全部数据提供整体视图
  • 用户可以逐步聚焦感兴趣的子集
  • 随时可以返回完整数据视图进行对比

25-search-box-exercise

搜索功能扩展

任务目标

  • 添加搜索输入控件,允许用户按 API 端点路径进行过滤
  • 实现基于文本的数据搜索功能
  • 结合复选框和搜索框创建多重过滤机制

inputs.search 控件介绍

  • 语法结构:类似于 inputs.checkbox 的使用方式
  • 搜索机制:对传入数据的所有字段进行文本匹配
  • 返回结果:匹配搜索条件的数据子集

搜索控件工作原理

viewof pathSearch = Inputs.search(
  apiLogData,  // 要搜索的数据集
  {
    // 配置选项
  }
)

预期功能效果

  • 用户输入"files"时显示所有文件相关端点
  • 用户输入"search"时显示搜索相关端点
  • 空搜索框显示所有数据
  • 搜索结果自动更新相关联的数据表格

多重过滤集成

组合过滤策略

将搜索框与之前的复选框控件结合:

  1. 搜索框过滤 API 路径
  2. 复选框过滤状态码
  3. 两个条件同时满足才显示数据点

实现思路

  • 将搜索结果作为基础数据集
  • 在搜索结果上应用状态码过滤
  • 确保图表反映两种过滤条件的交集

用户体验考虑

  • 渐进式过滤:用户可以先搜索端点,再选择状态码
  • 即时反馈:输入变化立即更新可视化
  • 直观操作:两种过滤方式互相补充,提供更精确的数据探索

26-search-box-solution

搜索功能实现

基本搜索设置

viewof pathSearch = Inputs.search(
  apiLogData,  // 替换空数组为实际数据
  {
    // 可选配置
  }
)

搜索结果验证

  • 无输入时:显示全部 2000 条记录
  • 输入"user":筛选出 83 条用户相关端点记录
  • 输入"search":显示 1,115 条搜索端点记录
  • 搜索结果pathSearch 变量包含过滤后的数据集

多重过滤实现

链式过滤逻辑

// 先应用搜索过滤,再应用状态码过滤
pathSearch.filter((d) => selectedCodes.includes(d.status));

关键实现要点

  • 数据源切换:从 apiLogData 改为 pathSearch
  • 过滤顺序:搜索过滤 → 状态码过滤
  • 响应式更新:任一过滤条件改变都会触发图表重绘

可视化效果分析

颜色统一现象

当搜索特定端点(如"search")时:

  • 所有数据点变为相同颜色
  • 原因:颜色通道映射到路径字段,相同路径产生相同颜色
  • 这实际上验证了过滤功能正确工作

交互式控制价值

用户现在可以:

  • 精确定位:聚焦特定 API 端点和状态码组合
  • 深度分析:专注于问题端点的性能表现
  • 灵活探索:根据需要调整过滤条件

Observable 生态系统优势

内置响应性

  • 自动同步:控件变化立即反映到可视化
  • 无缝集成:Plot 与 Inputs 天然协作
  • 开发效率:相比其他框架,减少状态管理复杂性

设计模式的通用性

这种模式可以扩展到其他场景:

  • 设备分辨率数据的多维度过滤
  • 用户行为数据的时间和类型筛选
  • 任何需要多条件数据探索的可视化项目

完整实现的技术价值

交互式仪表板能力

  • 用户主导:将数据探索权交给终端用户
  • 实时响应:输入变化的即时视觉反馈
  • 多维过滤:支持复合查询条件

代码复用性

建立的过滤和可视化模式可以:

  • 适应不同的数据集结构
  • 扩展到更多过滤维度
  • 作为构建复杂仪表板的基础框架

27-using-plot-with-vanilla-js-react

Observable 分享与嵌入

发布选项

私有协作

  • 通过"Share"功能与特定用户共享
  • 支持最多几个用户的私人协作空间

公开发布

  • Public:可发现,显示在用户档案中
  • Unlisted:仅通过链接访问,类似 YouTube 未列出视频

嵌入功能

  • iframe 嵌入:可将笔记本单元格嵌入其他网页
  • 实时交互:嵌入的可视化保持完整交互功能
  • Export 选项:通过三点菜单 → Export → Embed Cells

导出格式选择

<!-- iframe嵌入 -->
<iframe src="Observable嵌入URL"></iframe>

<!-- JavaScript代码片段 -->
// 包含Observable运行时的独立代码

<!-- React集成代码 -->
// 适配React组件的代码模板

独立使用 Plot

项目集成

# 包管理器安装
npm install @observablehq/plot
yarn add @observablehq/plot

// ES模块导入
import * as Plot from "@observablehq/plot";

核心实现模式

// 创建可视化
const chart = Plot.plot({
  marks: [
    Plot.dot(data, {
      x: "timestamp",
      y: "duration",
      fill: "path",
    }),
  ],
});

// 添加到DOM
document.getElementById("plot-container").appendChild(chart);

关键技术要点

  • 语法一致性:与 Observable 中的用法完全相同
  • DOM 操作:Plot 返回 SVG DOM 节点,需要手动添加到页面
  • 数据获取:需要自行处理数据加载(JSON 导入、API 调用等)

React 集成模式

核心 Hooks 使用

import { useRef, useEffect, useState } from "react";
import * as Plot from "@observablehq/plot";

function MyPlotComponent({ data }) {
  const containerRef = useRef();
  const [selectedCodes, setSelectedCodes] = useState(allStatusCodes);

  useEffect(() => {
    // 清除旧图表
    containerRef.current.innerHTML = "";

    // 创建新图表
    const chart = Plot.plot({
      marks: [
        Plot.dot(
          data.filter((d) => selectedCodes.includes(d.status)),
          { x: "timestamp", y: "duration", fill: "path" }
        ),
      ],
    });

    // 添加到容器
    containerRef.current.appendChild(chart);
  }, [data, selectedCodes]);

  return <div ref={containerRef}></div>;
}

React 特有考虑

  • useRef 逃逸:绕过 React 的 DOM 管理,为 Plot 提供安全绘制区域
  • useEffect 副作用:将图表绘制作为副作用处理
  • 状态管理:使用 React 状态替代 Observable 的响应式变量
  • 重绘处理:状态变化时清除旧图表,创建新图表

框架选择考虑

Observable 优势

  • 开箱即用:内置响应性和输入控件
  • 快速原型:适合数据探索和概念验证
  • 无需配置:自动处理依赖和状态管理

独立使用优势

  • 项目集成:与现有应用架构无缝融合
  • 完全控制:自定义数据获取和状态管理
  • 无外部依赖:不需要 Observable 运行时

选择指导原则

  • 探索阶段:使用 Observable 快速迭代和验证概念
  • 生产部署:迁移到合适的框架进行定制化开发
  • 混合策略:Observable 原型 → 框架重构

28-wrapping-up

核心学习成果总结

数据可视化的开发者价值

  • 开发流程优化:通过 CI 测试时间数据识别效率瓶颈
  • 用户体验改进:基于设备分辨率数据优化界面适配
  • 性能监控:利用 API 日志数据识别和解决性能问题
  • 交互式探索:构建灵活的数据钻取界面

技术能力建设

数据处理技能:

  • JavaScript 原生方法(filter、map)进行基础数据整理
  • 理解数据结构转换和预处理的重要性

可视化技术栈:

  • Observable Plot 的核心概念和语法
  • 响应式编程在数据可视化中的应用
  • 跨平台部署(Observable、Vanilla JS、React)

可视化设计思维框架

核心设计流程

  1. 问题定义:明确要回答的具体问题
  2. 数据整理:过滤、重塑数据以匹配分析需求
  3. 可视化选择:选择合适的图表类型(聚合 vs 个体数据点)
  4. 缩放策略:从数据域到显示域的映射方案
  5. 组织方式:决定数据聚合、分面或保持原样
  6. 交互设计:为用户提供必要的控制选项

关键决策考虑

  • 聚合 vs 分解:何时合并数据,何时拆分展示
  • 标记类型选择:点图、线图、区域图等的适用场景
  • 缩放类型:线性、对数等不同缩放的应用时机
  • 交互复杂度:平衡功能丰富性与用户体验简洁性

学习资源与进阶路径

即时参考资源

  • Observable Plot 速查表:可打印 PDF 或交互式笔记本版本
  • API 文档:GitHub 仓库中的完整技术参考
  • 示例库:Observable 团队提供的入门教程和视频

深度学习路径

D3.js 进阶

  • Plot 用于快速探索和原型验证
  • D3 用于自定义动画和复杂交互效果
  • 两者协作:Plot 探索 → D3 精细化

专业化方向

  • 时间序列数据可视化专题
  • 华盛顿大学 Observable 课程系列
  • Outlier 会议的行业专家分享

实践应用展望

立即可行的应用

  • 个人项目数据分析和展示
  • 团队内部数据监控仪表板
  • 客户端数据的可视化分析

技能转移价值

  • 数据驱动的决策制定能力
  • 复杂信息的清晰表达技能
  • 用户界面设计的数据敏感度

持续发展建议

  • 实践优先:在真实项目中应用所学概念
  • 社区参与:分享作品,获取反馈,学习他人经验
  • 技术栈扩展:根据项目需求选择合适的工具组合

这套完整的学习材料为从零基础到熟练应用建立了系统的知识框架,重点不仅在于工具使用,更在于培养数据可视化的设计思维和问题解决能力。