【D3.js in Action 3 精译_028】3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图
当前内容所在位置(可进入专栏查看其他译好的章节内容)
- 第一部分 D3.js 基础知识
- 第一章 D3.js 简介(已完结)
- 1.1 何为 D3.js?
- 1.2 D3 生态系统——入门须知
- 1.3 数据可视化最佳实践(上)
- 1.3 数据可视化最佳实践(下)
- 1.4 本章小结
- 第二章 DOM 的操作方法(已完结)
- 2.1 第一个 D3 可视化图表
- 2.2 环境准备
- 2.3 用 D3 选中页面元素
- 2.4 向选择集添加元素
- 2.5 用 D3 设置与修改元素属性
- 2.6 用 D3 设置与修改元素样式
- 2.7 本章小结
- 第三章 数据的处理 ✔️
- 3.1 理解数据(已完结)
- 3.2 准备数据(已完结)
- 3.3 将数据绑定到 DOM 元素(已完结)
- 3.3.1 利用数据给 DOM 属性动态赋值
- 【3.4 让数据适应屏幕】 ✔️
- 3.4.1 比例尺简介(上篇)
- 3.4.2 线性比例尺(中篇)
- 3.4.2.1 基于 Mocha 测试 D3 线性比例尺(DIY 实战)
- 3.4.2.2 使用 Observable 在线绘制 D3 条形图(DIY 实战) ✔️
- 3.4.3 分段比例尺(下篇)
- 3.5 加注图表标签(待翻译 ⏳)
- 3.6 本章小结
文章目录
- 3.4.2.2 使用 Observable 在线绘制 D3 条形图(DIY实战)
- 1 需求描述
- 2 操作步骤
- 2.1 加载数据
- 2.2 数据绑定
- 2.3 调整比例
- 3 小结 & 复盘

《D3.js in Action》全新第三版封面
3.4.2.2 使用 Observable 在线绘制 D3 条形图(DIY实战)
写在前面
作为本书第三章的有益补充,一名合格的数据可视化从业者应该时刻关注业界最新前沿趋势、主流观点、最佳实践等等。这当中就必然包括
Observable网站资源的合理利用。前面第一章介绍Observable笔记本功能(Notebooks)时,由于作者感觉偏离了本书重点,于是草草几句就结束了。今天就给大家打个样,去探探这个由D3.js创始人打造的Observable平台的深浅。
1 需求描述
根据目前的专栏进度,第三章的示例条形图除了数据标签还没实现,其他的部件基本都实现了。这次索性就把 D3 工作流那几个节点(如下图所示)从头到尾在 Observable 上实现一遍。

【图 1 要同步在 Observable 实现一遍的 D3 数据工作流关键节点】
2 操作步骤
首先登录 Observable 平台:https://observablehq.com/。用邮箱注册一个帐户或者直接关联 GitHub 帐号即可。登录成功后,从个人主页右上方新建的一个笔记本,如图 2 所示:

【图 2 新建一个 Observable 笔记】
在弹出窗口中为这个新笔记指定一个工作空间(Workspace),就相当于一个顶层文件夹。通常默认会选中一个,也可以手动指定。右边的模板就选默认的空白模板(Blank)即可,如图 3 所示:

【图 3 为新笔记文件指定一个工作空间】
点击 Create notebook 按钮,就正式进入 Observable 记事本的编辑页面了。此时会看到一个默认的 Markdown 格式的单元格,如图 4 所示:

【图 4 进入 Observable 记事本编辑页看到的第一个单元格】
那就用 Markdown 来一个华丽的标题吧,输入:# Ch3.4.3.1 DIY: Demo Bar Chart,然后按 Shift + Enter 快速执行,就有了下面的标题:

【图 5 使用 Markdown 语法添加的记事本一级标题】
2.1 加载数据
完成上面的准备工作后,就可以加载数据了。原始的 CSV 数据集需要先上传到 Observable 网页,即右侧那个回形针图标:

【图 6 将 CSV 原始数据集上传到 Observable 页面】
上传完毕后,可以看到这个文件的相关信息,显示还未被使用:

【图 7 文件上传完毕时的页面提示信息】
这个数据集该怎么加载,变成我们需要的对象数组呢?熟悉 Python Jupyter Lab 的朋友肯定很熟悉这样的界面,其实 Observable 就是一个 JavaScript 版的 Jupyter Lab,所有数据操作都在单元格内进行。Observable 提供了 FileAttachment 接口来加载数据,要在 JavaScript 单元格内这样写:
FileAttachment('data.csv')
但这样得到的数据值全是字符串类型的,因此还需要调用一个 csv() 方法,并传入一个配置项,实现简单格式的自动转换:
FileAttachment('data.csv').csv({typed: true});
然后将结果赋给一个常量 data。注意,这里无需添加 const 或 let 关键字,声明的变量类似 Excel 里的单元格的值,可以全局引用。此外,Observable 的单元格严格遵循“单一职责”原则,因此最好写成 IIFE 形式:
data = (async function(){const data = await FileAttachment('data.csv').csv({typed: true});return data.sort((a, b) => d3.descending(a.count, b.count));
})();
这样,数据加载 + 转换 + 排序就一气呵成轻松实现了!我这里的排序还用到了一个新的工具函数:d3.descending(a.count, b.count)。仔细看,其实我并没有手动导入 D3 这个库,之所以能直接使用,是因为 Observable 已经内置了最新版的 D3.js(别人好歹是创始人,这点特权还是有的)。最后执行单元格的效果如下:

【图 8 完成数据加载、转换、排序后的 Observable 页面效果截图】
2.2 数据绑定
刚才一个单元格就实现了 D3 工作流的三个节点(即查找(文件上传)、加载、格式化),个人感觉比手动去调 Promise 省事多了。下一步就该定义 SVG 容器并绑定数据了。Observable 单元格支持三大基本类型:JavaScript 型、HTML 型以及 Markdown 型。按照书中的方法创建一个 div 元素倒也不是不行,只要在 HTML 格式的单元格里创建 div,再把样式写到一个 style 标签里就搞定了。但是 Observable 提供了更快的方式:
chart = {const svg = d3.create('svg').attr('viewBox', `0 0 600 700`).style('border', '1px solid black').style('max-width', 800).style('margin-inline', 'auto');return svg.node();
}
这段代码的最后一句,相当于导出 DOM 节点并赋值给变量 chart。同样按下 Shift + Enter,页面立刻渲染出了指定的容器:

【图 10 执行 d3.create() 方法可以快速创建 SVG 容器】
因此,数据绑定逻辑只要稍加改动就行了:

【图 11 改造后的 createViz 方法及其调用的写法】
剩下的工作就简单多了(其实整体来讲也没啥难度)。
2.3 调整比例
于是来到 D3 数据标准工作流的最后一环:调整比例大小。先定义好两个比例尺:
const xScale = d3.scaleLinear().domain([0, data[0].count]).range([0, 450]);const yScale = d3.scaleBand().domain(data.map(d => d.technology)).range([0, 700]).paddingInner(0.2);
由于 data 已经按 count 票数值降序排列,这里水平方向的定义域上边界就是 data[0].count 。接着再使用 D3 的数据绑定语法完成条形图的渲染:
const byTechName = ({technology: t}) =>(t === 'D3.js') ? 'yellowgreen' : 'skyblue';
svg.selectAll('rect').data(data).join('rect').attr('x', 100).attr('y', d => yScale(d.technology)).attr('height', yScale.bandwidth()).attr('width', d => xScale(d.count)).attr('fill', byTechName);
这里填充色的设置做了点小改动,提出了一个访问器函数(accessor function)byTechName,这样写起来更紧凑。最终完整的 createViz 逻辑如下:
createViz = (data, svg) => {// Declare D3's scale functionsconst xScale = d3.scaleLinear().domain([0, data[0].count]).range([0, 450]);const yScale = d3.scaleBand().domain(data.map(d => d.technology)).range([0, 700]).paddingInner(0.2); // the gap size between 2 bars// data binding logics ...const byTechName = ({technology: t}) =>(t === 'D3.js') ? 'yellowgreen' : 'skyblue';svg.selectAll('rect').data(data).join('rect').attr('x', 100).attr('y', d => yScale(d.technology)).attr('height', yScale.bandwidth()) // i.e. bar height.attr('width', d => xScale(d.count)).attr('fill', byTechName);return svg.node();
};
运行该单元格,上面定义的 chart 单元格就渲染出了想要的条形图:

【图 12 最终在 Observable 的记事本页面渲染出的条形图效果】
3 小结 & 复盘
以上演示内容还只是 Observable 平台功能的冰山一角,根据最新发布的新闻,Observable 不久前已经上线了自己的云平台 Observable Cloud,可以像 GitHub、npm 那样在线托管自己的可视化项目,并且提供了大量实用的工具,比如接入了 AI 的能力,专为数据应用量身打造的线上工作流等等。浏览过程中,我发现 D3 官网还没有和 Vue.js 做集成的相关内容,而且 Notebook 页面好像对 Mocha.js 这类测试框架不是很友好,目前只测通了断言库 Chai.js。窃以为,这些地方恰恰蕴藏着大量的机遇,值得各位 D3 爱好者们前来探索!
最后还是梳理成要点,谈谈这趟试水 Observable 平台的切身体会吧:
- 大胆尝试
Observable环境,遇到问题再小心求证; - 多使用
Observable提供的便捷接口、工具函数。D3.js的官方入门文档(https://d3js.org/getting-started)是个不错的切入口; - 积极拥抱 D3 生态,不要拘泥于一本书、或者某个单一的环境;
Observable创建的记事本还能共享给多人访问,实现同步编辑、实时预览;- 多从线上的优秀案例学习
D3.js的不同写法,取长补短,融会贯通。
(完)
相关文章:
【D3.js in Action 3 精译_028】3.4 小节 DIY 实战:使用 Observable 在线绘制 D3 条形图
当前内容所在位置(可进入专栏查看其他译好的章节内容) 第一部分 D3.js 基础知识 第一章 D3.js 简介(已完结) 1.1 何为 D3.js?1.2 D3 生态系统——入门须知1.3 数据可视化最佳实践(上)1.3 数据可…...
【Linux】 TCP短服务编写和守护进程
文章目录 TCP 短服务编写流程进程组和会话和守护进程 TCP 短服务编写流程 TCP服务器是面向连接的,客户端在发送数据之前需要先与服务器建立连接。 因此,TCP服务器需要能够监听客户端的连接请求。为了实现这一功能,需要将TCP服务器创建的套接字…...
自学数据库-MYSQL
自学数据库-MYSQL 一.表和视图1.表1.1 表创建1.2 索引1.2.1 这里是废话,不感兴趣的可以直接更具目录的跳过这里的内容1.2.1.1 索引是什么1.2.1.2 相关数据结构:二叉树、红黑树、B-Tree、BTree、Hash…①普通索引②唯一索引③全文索引④组合索引 1.3 表数据操作(更新…...
机器学习——多模态学习
多模态学习:机器学习领域的新视野 引言 多模态学习(Multimodal Learning)是机器学习中的一个前沿领域,它涉及处理和整合来自多个数据模式(如图像、文本、音频等)的信息。随着深度学习的蓬勃发展࿰…...
ceph掉电后无法启动osd,pgs unknown
处理办法: 只有1个osd,单副本,掉电损坏osd,只能考虑重建pg,丢失部分数据了。生产环境务必考虑2,3副本设计。避免掉电故障风险。 掉电后osdmap丢失无法启动osd的解决方案 - 武汉-磨渣 - 博客园 https://zhuanlan.zhih…...
HTML5实现古典音乐网站源码模板1
文章目录 1.设计来源1.1 网站首页1.2 古典音乐界面1.3 著名人物界面1.4 古典乐器界面1.5 历史起源界面2.效果和源码2.1 动态效果2.2 源代码源码下载万套模板,程序开发,在线开发,在线沟通作者:xcLeigh 文章地址:https://blog.csdn.net/weixin_43151418/article/details/142…...
快速生成单元测试
1. Squaretest插件 2. 依赖 <dependency><groupId>junit</groupId>...
WebGL系列教程十一(光照原理及Blinn Phong着色模型)
快速导航(持续更新中) WebGL系列教程一(开篇) WebGL系列教程二(环境搭建及着色器初始化) WebGL系列教程三(使用缓冲区绘制三角形) WebGL系列教程四(绘制彩色三角形&…...
《ASP.NET Web Forms 实现短视频点赞功能的完整示例》
在现代Web开发中,实现一个动态的点赞功能是非常常见的需求。本文将详细介绍如何在ASP.NET Web Forms中实现一个视频点赞功能,包括前端页面的展示和后端的处理逻辑。我们将确保点赞数量能够实时更新,而无需刷新整个页面。 技术栈 ASP.NET We…...
Linux SSH服务
Linux SSH(Secure Shell)服务是一种安全的远程登录协议,用于在Linux操作系统上远程登录和执行命令。它提供了加密的通信通道,可以在不安全的网络环境中安全地进行远程访问。 SSH服务在Linux系统中通常使用OpenSSH软件包来实现。它…...
MySQL--视图(详解)
目录 一、前言二、视图2.1概念2.2语法2.3创建视图2.3.1目的 2.4查看视图2.5修改数据2.5.1通过真实表修改数据,会影响视图2.5.2通过修改视图,会影响基表 2.6注意2.7 删除视图2.8 视图的优点 一、前言 欢迎大家来到权权的博客~欢迎大家对我的博客进行指导&…...
Javascript 普通非async函数调用async函数
假设我们有一个异步函数 async function asyncFunction() {console.log("开始执行异步函数");await new Promise(resolve > setTimeout(resolve, 1000)); // 模拟异步操作console.log("异步函数执行完毕"); } 我们在调用这个异步函数时,比…...
【LeetCode】修炼之路-0004-Median of Two Sorted Arrays【python】
题目 Given two sorted arrays nums1 and nums2 of size m and n respectively, return the median of the two sorted arrays. The overall run time complexity should be O(log (mn)). Example 1: Input: nums1 [1,3], nums2 [2] Output: 2.00000 Explanation: merged…...
C++面试速通宝典——10
177. #include <filename> 和 #include "filname.h" 有什么区别? 对于 #include <filename> , 编译器从标准库路径开始搜索 filename.h。 对于 #include "filename.h,编译器从用户的工作…...
肺腺癌预后新指标:全切片图像中三级淋巴结构密度的自动化量化|文献精析·24-10-09
小罗碎碎念 本期这篇文章,我去年分享过一次。当时发表在知乎上,没有标记参考文献,配图的清晰度也不够,并且分析的还不透彻,所以趁着国庆假期重新分析一下。 这篇文章的标题为《Computerized tertiary lymphoid structu…...
基于jmeter+perfmon的稳定性测试记录
1. 引子 最近承接了项目中一些性能测试的任务,因此决定记录一下,将测试的过程和一些心得收录下来。 说起来性能测试算是软件测试行业内,有些特殊的部分。这部分的测试活动,与传统的测试任务差别是比较大的,也比较依赖…...
前沿论文 M5Product 组会 PPT
对比学习(Contrast learning):对比学习是一种自监督学习方法,用于在没有标签的情况下,通过让模型学习哪些数据点相似或不同来学习数据集的一般特征。假设一个试图理解世界的新生婴儿。在家里,假设有两只猫和…...
navicat~导出数据库密码
当我们mysql密码忘记了,而在navicat里有记录,我们应该如何导出这个密码呢? 第一步:文件菜单,导出链接,导出连接获取到 connections.ncx 文件 这里需要勾选 导出密码!!! 不然导出的文…...
【Java】 —— 数据结构与集合源码:Vector、LinkedList在JDK8中的源码剖析
目录 7.2.4 Vector部分源码分析 7.3 链表LinkedList 7.3.1 链表与动态数组的区别 7.3.2 LinkedList源码分析 启示与开发建议 7.2.4 Vector部分源码分析 jdk1.8.0_271中: //属性 protected Object[] elementData; protected int elementCount;//构造器 public …...
YOLOv5改进——添加SimAM注意力机制
目录 一、SimAM注意力机制核心代码 二、修改common.py 三、修改yolo.py 三、建立yaml文件 四、验证 一、SimAM注意力机制核心代码 在models文件夹下新建modules文件夹,在modules文件夹下新建一个py文件。这里为simam.py。复制以下代码到文件里面。 import…...
留学生如何应对Turnitin检测升级:实测防翻车的3款高效降AI工具
马上就要汇报了,不知道屏幕前的你,手里的文章彻底定稿了没有? 最近这段时间,大家是不是还在为居高不下的 AI 率发愁。特别是对于需要过 Turnitin 检测的伙伴来说,明明都是自己查资料敲出来的稿件,AI疑似率依…...
别再手动算矩阵了!COMSOL中矢量与矩阵变换的保姆级配置指南(附避坑点)
COMSOL中矢量与矩阵变换的高效配置与实战避坑指南 在COMSOL Multiphysics的建模过程中,矢量与矩阵操作是处理复杂物理场问题的核心技能之一。许多工程师和研究人员在初次接触COMSOL的变量定义系统时,往往会陷入一个误区——试图像常规编程语言那样直接定…...
快速 AI 迭代仍然需要操作纪律
前言 配套资源:AI 辅助开发检查清单资源包,适合把本文的流程直接落成开发前检查表和复盘模板。 上一篇文章里,我把 AI 工作流拆成了几类任务模式:开发维护、探索学习、反馈确认。这个分类解决的是一个前置问题:在使用 …...
机器学习_03_线性回归
线性回归一、概念与定位类型:监督学习、回归任务定义:用于建模【特征 X】与【连续标签 y】之间的【线性关系】核心思想:找一条直线(或超平面),让预测值 ŷ 与真实值 y 的【误差最小】二、模型形式一元线性回…...
别再只用ARIMA了!用Facebook Prophet快速搞定业务时间序列预测(附Python实战代码)
用Facebook Prophet三行代码完成高精度业务预测:电商场景实战指南 当市场部门的同事又在周五下午5点发来"下周销售预测急用"的邮件时,你是否还在为ARIMA模型的参数调优焦头烂额?时间序列预测本应是数据科学中最具商业价值的技能之一…...
电路分析基础(2)
受控源 基本概念 理想受控源模型...
别再用strlen了!C++里sizeof和字符数组的坑,我帮你踩完了
别再用strlen了!C里sizeof和字符数组的坑,我帮你踩完了 在C编程中,处理字符串和字符数组时,sizeof和strlen这两个看似简单的概念常常让初学者陷入困惑。特别是在信息学竞赛或日常编程中,错误地使用它们可能导致难以察…...
解决ubuntu中hermes agent连接taotoken自定义供应商的配置问题
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 解决 Ubuntu 中 Hermes Agent 连接 Taotoken 自定义供应商的配置问题 如果你在 Ubuntu 系统上使用 Hermes Agent,并希望…...
告别HAL库:用GD32标准库为RT-Thread打造轻量级驱动(以F4系列为例)
告别HAL库:用GD32标准库为RT-Thread打造轻量级驱动(以F4系列为例) 在嵌入式开发领域,HAL库因其跨平台兼容性和易用性广受欢迎,但对于追求极致性能和精简代码的开发者而言,标准库往往能带来更直接的硬件控制…...
【亲测免费】 ImageNet标签文件及读取脚本:加速您的计算机视觉研究
ImageNet标签文件及读取脚本:加速您的计算机视觉研究 【下载地址】ImageNet标签文件及读取脚本 ImageNet 标签文件及读取脚本 项目地址: https://gitcode.com/open-source-toolkit/56c9e 项目介绍 在计算机视觉领域,ImageNet数据集是图像分类任务…...
