【D3.js in Action 3 精译_033】4.1.0 DIY 实战:如何通过学习 d3.autoType 函数深度参与 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.3 分段比例尺(下篇)
          • 3.4.3.1 使用 Observable 在线绘制 D3 条形图(DIY 实战)
      • 3.5 加注图表标签(上篇)
        • 3.5.1 人物专访:Krisztina Szűcs(下篇)
      • 3.6 本章小结
    • 第四章 直线、曲线与弧线的绘制 ✔️
      • 4.1 坐标轴的创建(上篇)
        • 4.1.0 DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设 ✔️
        • 4.1.1 D3 中的边距约定(精译中 ⏳)
        • 4.1.2 坐标轴的生成
      • 4.2 D3 折线图的绘制

文章目录

  • DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设
    • 1 起因
    • 2 经过
      • 2.1 日期转换中的坑
      • 2.2 关于“中规中矩”
      • 2.3 直奔源码
      • 2.4 issue 溯源
      • 2.5 踏上单元测试的漫漫征途
      • 2.6 提交 PR
    • 3 结尾

DIY 实战:如何通过学习 d3.autoType 函数深度参与 D3 生态建设

1 起因

上一篇(即本专栏第 032 篇)谈到过一个快速转换数据类型的工具函数 d3.autoType,当时作者推荐了一篇发表在 Observable 的文章,还说它是一篇写得很棒的文章(a great article)。今天就来研究研究这篇文章怎么个好法,顺便梳理一下身为码畜的本人是怎么通过这篇文章深度参与 D3.js 生态建设的(算是二度抛砖引玉吧)。

2 经过

文章链接在这里:https://observablehq.com/@d3/d3-autotype。打开一看,原来是 D3.js 的创始人 Mike Bostock 的大作!文章写于 2019 年 2 月 8 日,最后一次更新是在 2022 年 5 月 31 日(莫非是大佬送给自己的六一儿童节礼物?)。咳咳,说正题。

原来,d3.autoType 是 d3-dsv 模块下的一个工具函数,支持简单的类型推断(automatic type inference),前面先介绍它的诞生背景——让开发者从简单而繁琐的手动类型转换中解放出来,能转成数字、日期以及布尔值的就直接转了。

但问题也接踵而至:万一我要的是日期,自动转换却给我一个数字咋整?

还能咋整,特殊情况特殊处理呗,转完再手动检查一遍(这个环节不妨就叫 真·人工智能)。

2.1 日期转换中的坑

以上都是与《D3.js in Action》书中重复的内容。接下来,Mike 大佬提到了原生 JavaScript 一个奇葩的点:在解析成日期的时候,只有年月日的字符型日期(比如 "2024-10-11")会以子午本初线上的 0 时差为准;而带有时分秒这类后缀的(如 "2024-10-11T00:00"),则会默认按当地时间来转换,即结果带时差。大佬都说没办法了,这是 ECMAScript 规范写好的,强大如 Observable 也只能照办。所以就会出现这样奇葩结果:

图1 d3.autoType 在处理日期和时间戳时由于 ECMAScript 规范导致的转换不一致问题

【图1 d3.autoType 在处理日期和时间戳时由于 ECMAScript 规范导致的转换不一致问题】

其实也不难理解,一般转换个时间戳,谁会希望拿到一个和所在地区隔了 16 小时时差的结果呢?只是日期按子午线的时差来算,确实有点强人所难了。

这个坑算是原生 JavaScript 挖的,今后注意便是了。

2.2 关于“中规中矩”

文章还说,d3.autoType 还支持美国各州县的五位邮政编码转换。六位就不行?赶紧测一下:

图 2 d3.autoType 转换字符型编码没有预想中的五位数限制

【图 2 d3.autoType 转换字符型编码没有预想中的五位数限制】

那么,不是那么中规中矩的呢?它会直接跳过——

图 3 对于不那么中规中矩的数字字符串,d3.autoType 直接跳过

【图 3 对于不那么中规中矩的数字字符串,d3.autoType 直接跳过】

不仅数字如此,日期和布尔值也一样——

图 4 对于不那么中规中矩的日期和布尔值,d3.autoType 也直接跳过

【图 4 对于不那么中规中矩的日期和布尔值,d3.autoType 也直接跳过】

最后,Mike 总结了一下 d3.autoType 能转的类型:

  1. If empty, then null.(为空的,就转为 null
  2. If exactly “true”, then true.(严格写作 "true" 的,才转为 true
  3. If exactly “false”, then false.(严格写作 "false" 的,才转为 false
  4. If exactly “NaN”, then NaN.(严格写作 "NaN"的,才转为 NaN
  5. Otherwise, if coercible to a number, then a number.(以上都不行,但能强转为数字的,才转为数字)
  6. Otherwise, if a date-only or date-time string, then a Date.(以上再不行,但只有日期或者带时间的日期字符串,才转为 Date 型)
  7. Otherwise, a string (the original untrimmed value).(以上都转不了的,还转个啥,摆烂吧)

2.3 直奔源码

既然来都来了,怎么能止步于上面列出的这几条八股文呢?果断扒出 d3.autoType 的源码(根据开头提到的 d3-dsv 模块按图索骥):

// Path: d3-dsv/src/autoType.js
export default function autoType(object) {for (var key in object) {var value = object[key].trim(), number, m;if (!value) value = null;else if (value === "true") value = true;else if (value === "false") value = false;else if (value === "NaN") value = NaN;else if (!isNaN(number = +value)) value = number;else if (m = value.match(/^([-+]\d{2})?\d{4}(-\d{2}(-\d{2})?)?(T\d{2}:\d{2}(:\d{2}(\.\d{3})?)?(Z|[-+]\d{2}:\d{2})?)?$/)) {if (fixtz && !!m[4] && !m[7]) value = value.replace(/-/g, "/").replace(/T/, " ");value = new Date(value);}else continue;object[key] = value;}return object;
}// https://github.com/d3/d3-dsv/issues/45
const fixtz = new Date("2019-01-01T00:00").getHours() || new Date("2019-07-01T00:00").getHours();

真相大白了:原来转为数字的时候,用的是 number = +value(第 9 行),即便前面写再多的 0 也是徒劳。

最长的那行正则表达式,就是处理日期的,确实麻烦了点。扔给 AI 吧,我才懒得去匹配每一个捕获组呢:

图 5 AI 对源码正则表达式的解读结果

【图 5 AI 对源码正则表达式的解读结果】

解释得还不赖。既然它这么懂源码,今后类似的活就交给它吧(有没有一种似曾相识的赶脚?)。

最后剩下那个 fixtz 常量,为啥要把 2019 年 1 月 1 号和 7 月 1 号写到一起呢?去看看上面的 issue 提案就知道了:https://github.com/d3/d3-dsv/issues/45。

这一看,便如同打开了潘多拉的魔盒……

2.4 issue 溯源

原来,这个 45 号提案已经关闭了,最后是由 53 号提案解决的。该 45 号提案还是 Mike 本人提出的:

图 6 无意间看到的作者个人简介,迷之搞笑~

【图 6 无意间看到的作者个人简介,迷之搞笑~】

他说 Safari 浏览器误将带时间的字符串默认按 UTC 子午本初线上的时差来考虑了,并且备注说这是浏览器自己的漏洞,改起来也简单(难道 Safari 是想以一己之力纠正 ECMAScript 这个历史遗留问题?)。之后有个叫 Fil 的开发者提了第 51 号提案,验证了这个说法并尝试进行修复。Fil 认为,通过分别检验冬时令和夏时令 某个日期的 00:00 零点时刻 是否真的为零,就可以复现 Safari 这个漏洞,但他不知道怎么在 node 环境下写测试用例,只在 Observable 上调通了 一个版本(可惜失效了,看不到最初的改动)。Mike 顺着这个思路对原函数进行了几处优化,经过多次沟通,最终给出了现在看到的版本。

2.5 踏上单元测试的漫漫征途

本以为事情圆满结束了,结果顺着 51 号提案的跟帖,又看到了该问题单元测试的一个修复过程。大致意思是,源码的 Bug 修复了,但是单元测试却失败了:

# (运行命令:yarn test)
# csvFormat(array) converts dates to ISO 8601
ok 131 should be equivalent
not ok 132 should be equivalent---operator: deepEqualexpected: |-'date\n2018-01-01T08:00Z'actual: |-'date\n2017-12-31T23:00Z'at: Test.<anonymous> (/Users/fil/Sites/d3/d3-dsv/test/csv-test.js:263:8)stack: |-Error: should be equivalent
...

根据回复中摘录的断言信息,我很快锁定了报错的测试用例(第 3 行报错):

it("csvFormat(array) converts dates to ISO 8601", () => {assert.deepStrictEqual(csvFormat([{date: new Date(Date.UTC(2018, 0, 1))}]), "date\n2018-01-01");assert.deepStrictEqual(csvFormat([{date: new Date(2018, 0, 1)}]), "date\n2018-01-01T08:00Z");
});

大佬就是大佬,Mike 一下子就看出了问题:命令行的默认时区可能和测试用例的不一致。他建议在测试脚本中硬编码一个时区,就像测试 d3-time 模块时那样:

图 7 Mike Bostock 给出的单元测试修复意见

【图 7 Mike Bostock 给出的单元测试修复意见】

要不怎么说姜还是老的辣呢,原来类似的问题早就处理过了,不用重复造轮子。在大神上帝视角般的关照下,幸运的 Fil 终于通过了测试,回复标题上都难掩激动之情:

图 8 Fil 通过测试后的回复标题(地球上任何地方都能跑通测试了)

【图 8 Fil 通过测试后的回复标题(地球上任何地方都能跑通测试了)】

有这么夸张吗?点开一看,原来是在 package.jsontest 脚本的开头加了一个 TZ=America/Los_Angeles,即 Mike 大神说的硬编码。

加了这玩意儿就真的能在我笔记本里运行?开什么玩笑?PowerShell 知道 TZ 是啥吗?带着一连串的问题,我把 d3-dsv 的代码拷到了本地:

$ git clone https://github.com/d3/d3-dsv.git
$ cd d3-dsv
$ npm install
$ npm run test> d3-dsv@3.0.1 test
> TZ=America/Los_Angeles mocha 'test/**/*-test.js' && eslint src test'TZ' 不是内部或外部命令,也不是可运行的程序
或批处理文件。

怎么样?尽吹牛!还 run tests everywhere on the planet 呢,这不啪啪打脸吗?!?!

发现新大陆的惊喜是有的,但转瞬即逝:这个问题怎么解决?

既然问题是由 Safari 浏览器引发的,是否可以推断他俩的操作系统都是 Linux / Unix 这一路的?换成 Linux 就好使了?切换到 Windows 自带的 Linux 环境(即 Windows Subsystem for Linux,简称 WSL),又运行了一遍。没想到又双叒叕猜中了(之前的股票基金咋没那么神呢?):

图 9 换到 WSL 环境下运行的单元测试结果图

【图 9 换到 WSL 环境下运行的单元测试结果图】

这么一折腾,问题反而简单了:只要让 PowerShell 能运行 Linux 下的这个命令就可以了。这都不用问 AI,之前就遇到过这样的问题,加个开发依赖就搞定了:

# (under PowerShell project root)
$ npm i -D cross-env
$ (Get-Content package.json) -replace '"test": "(.*?)"', '"test": "cross-env $1"' | Set-Content package.json
$ npm run test

成败在此一举:

图 10 换回 Windows 环境下运行的单元测试结果图

【图 10 换回 Windows 环境下运行的单元测试结果图】

至此,才算真正破案了。

2.6 提交 PR

都测到这份上了,就提一个 pull request 吧。说干就干:

图 11 正式提交到 d3-dsv 官方仓库下的拉取请求(pull-request)页截图

【图 11 正式提交到 d3-dsv 官方仓库下的拉取请求(pull-request)页截图】

3 结尾

以下是此次刨根问底的几点体会:

  • 不要轻易放过《D3.js in Action》中推荐的资源链接(比如 Mike Bostock 那篇文章);
  • 边学边动手实操;
  • 尽量找到问题对应的源码;
  • 从相关的 issue 提案、pull-request 议案中厘清事情的来龙去脉;
  • 积极参与,并尝试贡献自己的开源代码。

经此一役,d3.autoType 这个知识点里的坑,我相信这辈子都不会踩第二遍了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/56079.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于YOLO11/v10/v8/v5深度学习的安检X光危险品检测与识别系统设计与实现【python源码+Pyqt5界面+数据集+训练代码】

《博主简介》 小伙伴们好&#xff0c;我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源&#xff0c;可关注公-仲-hao:【阿旭算法与机器学习】&#xff0c;共同学习交流~ &#x1f44d;感谢小伙伴们点赞、关注&#xff01; 《------往期经典推…

Microsoft Visual Studio安装gtest

1. 参考【Windows Visual Studio下安装和使用google test&#xff08;gtest&#xff09;】 https://blog.csdn.net/Bule_Zst/article/details/78420894 2. 编译gtest使用Win32模式。 3. 配置属性&#xff0c;C/C&#xff0c;常规&#xff0c;附加包含目录 …

Zilliz获Forrester报告全球第一;OB支持向量能力;Azure发布DiskANN;阿里云PG发布内置分析引擎

重要更新 1. Azure发布PostgreSQL向量索引扩展DiskANN&#xff0c;声称在构建HNSW/IVFFlat索引上&#xff0c;速度、精准度都超越pg_vector&#xff0c;并解决了pg_vector长期存在的偶发性返回错误结果的问题( [1] )。 2. 阿里云RDS PostgreSQL 发布AP加速引擎&#xff08;rds…

《Programming from the Ground Up》读后感

之所以看这本书&#xff0c;是想了解一些跟汇编相关的知识&#xff0c;打开这本书后就被作者的观点——“If you don’t understand something the first time, reread it. If you still don’t understand it, it is sometimes best to take it by faith and come back to it …

qemu启动busybox虚拟机网络连接配置

一、busybox文件系统网络问题 由于根文件是用busybox构建&#xff0c;所以很多配置文件是没有的&#xff0c;包括部分网络的默认设置。启动虚拟机后只能使用ip命令和ifconfig命令查看网络状态。 二、开启qemu网络支持 想要使虚拟机上网&#xff0c;最简单的方式可以使用 -netde…

Opencv中的直方图(3)直方图比较函数compareHist()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 比较两个直方图。 函数 cv::compareHist 使用指定的方法比较两个密集或两个稀疏直方图。 该函数返回 d ( H 1 , H 2 ) d(H_1, H_2) d(H1​,H2​…

南科大分享|大数据技术如何赋能大模型训练及开发

嘉宾介绍 张松昕&#xff0c;南方科技大学统计与数据科学系研究学者&#xff0c;UCloud 顾问资深算法专家&#xff0c;曾任粤港澳大湾区数字经济研究院访问学者&#xff0c;主导大模型高效分布式训练框架的开发&#xff0c;设计了 SUS-Chat-34B 的微调流程&#xff0c;登顶 Ope…

基于深度学习的图像背景去除系统

项目介绍 该项目的主要目标是开发一种高效、准确且适用于高分辨率的抠图系统&#xff0c;能够在自然场景中精确定位并分割出图像中的人、动物、建筑等一系列目标物体。 技术介绍 本项目使用的是基于python的pytorch神经网络框架&#xff0c;使用的神经网络是基于Resnet-50的…

[mysql]多表查询详解

我们如果要查询,我们就要用 SELECT .... FROM .... WHERE AND/OR/NOT #我们需要用过滤的条件来对数据进行筛选,不然会有很多多余数据 ORDER BY (ASC/DESC)#排序 LIMIT....,#是在几个有限的数据库管理系统里所以,PGsql,mysql,等 多表查询的意义 我们目前为止的查询语句…

Docker理念

1.为什么会出现Docker Docker 的出现并非偶然&#xff0c;而是由一系列技术发展趋势和实际需求所推动的一项技术创新。 随着软件行业的快速发展&#xff0c;开发团队的规模不断扩大&#xff0c;成员可能分布在不同的地理位置&#xff0c;使用不同的操作系统和开发工具。这就导致…

AI预测体彩排3采取888=3策略+和值012路或胆码测试10月11日升级新模型预测第101弹

经过100多期的测试&#xff0c;当然有很多彩友也一直在观察我每天发的预测结果&#xff0c;得到了一个非常有价值的信息&#xff0c;那就是9码定位的命中率非常高&#xff0c;已到达90%的命中率&#xff0c;这给喜欢打私菜的朋友提供了极高价值的预测结果~当然了&#xff0c;大…

AI核身-金融场景凭证篡改检测Baseline实践

金融领域交互式自证业务中涵盖信用成长、用户开户、商家入驻、职业认证、商户解限等多种应用场景&#xff0c;通常都需要用户提交一定的材料&#xff08;即凭证&#xff09;用于证明资产收入信息、身份信息、所有权信息、交易信息、资质信息等&#xff0c;而凭证的真实性一直是…

selenium自动化测试之Junit

1. 常用的注解 将junit的索引添加到pom文件&#xff1a; <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api --><dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter-api</artifactId&…

基于Java实现(APP)智能停车场管理系统

移动应用开发系统设计说明书&#xff08;智能停车场管理系统&#xff09; 服务集成流程详细设计 实现功能 序号功能点1新增用户2注册用户3修改场地信息4列出场地信息5新增认证车辆6列出认证车辆7删除认证车辆8车辆进入信息录入9停车记录列表展示10出停车场信息录入 参数说明…

YOLOv8实战PCB电路板缺陷检测【数据集+YOLOv8模型+源码+PyQt5界面】

本文采用YOLOv8作为核心算法框架&#xff0c;结合PyQt5构建用户界面&#xff0c;使用Python3进行开发。YOLOv8以其高效的实时检测能力&#xff0c;在多个目标检测任务中展现出卓越性能。本研究针对PCB电路板缺陷数据集进行训练和优化&#xff0c;该数据集包含丰富的PCB电路板缺…

【计网】从零开始学习http协议 ---深入理解cookie和session

我的天空里没有太阳&#xff0c; 总是黑夜&#xff0c; 但并不暗&#xff0c; 因为有东西代替了太阳。 --- 东野圭吾 --- 从零开始学习http协议 1 理解cookie1.1 什么是cookie1.2 验证cookie1.3 cookie的属性 2 理解session2.1 什么是session2.2 验证session 1 理解cooki…

刷题 链表

面试经典150题 - 链表 141. 环形链表 class Solution { public:bool hasCycle(ListNode *head) {ListNode* slow head, *fast head;while (fast ! nullptr && fast->next ! nullptr) {slow slow->next;fast fast->next->next;if (slow fast) {return…

【Linux复习】指令

文章目录 1.>2. cat3.系统命令bash和shell和kernel权限只被认证一次粘滞位引入前提知识场景解释为什么普通用户&#xff08;无w权限&#xff09;可以删除文件&#xff1f;为什么普通用户通过sudo设置文件权限为000后仍能删除文件&#xff1f; 结论 粘滞位是干什么的&#xf…

苍穹外卖P19--异常处理

以上传数据库数据重复为例&#xff0c;进行异常处理。 接口文档报错&#xff1a; 重新启动&#xff1a;

单例模式和读者写者问题

文章目录 10. 线程安全的单例模式10.1 什么是设计模式10.2 什么是单例模式10.3 单例模式的特点10.4 饿汉方式和懒汉方式10.5 单例模式的线程池 11. STL和智能指针的线程安全 问题11.1 STL中的容器是否是线程安全的?11.2 智能指针是否是线程安全的? 12. 其他常见的各种锁13. 读…