D3 中 d3-array 刻度(Ticks)API 详解:ticks、tickStep、nice 与 range 的完整使用指南
本文围绕 d3 官方文档 docs/d3-array/ticks.md 展开,系统讲解 d3-array 提供的五个刻度相关 API:d3.ticks、d3.tickIncrement、d3.tickStep、d3.nice 和 d3.range。读完本文,你将理解 d3 如何为连续数值区间生成"整齐、等距、数量约可控"的刻度值,掌握 tickIncrement 返回负逆步长的 IEEE 754 精度设计意图,并知道如何把这些函数与 d3-scale 线性比例尺、d3-axis 坐标轴及 d3-format 组合,产出可直接用于图表的刻度与标签。
概览:这组 API 解决什么问题
在图表中,给定数据区间(如坐标轴域 [1, 9]),我们需要一组"好看的"刻度值:等距、数量接近期望值、且为人类友好的整数或半整数。d3-array 把这组能力拆成两个正交的函数族:
- 刻度生成族:
d3.ticks返回刻度值数组;d3.tickStep返回相邻刻度的步长;d3.tickIncrement是步长的整数化变体(供高精度内部计算使用)。 - 区间扩展族:
d3.nice把一个任意区间向外扩成与刻度步长对齐的"整齐"区间。 - 等差数列族:
d3.range生成等差序列,类似 Python 内置range,常用于索引遍历或按固定步长取样。
从 src/index.js 可以看到,d3 主包以 export * from "d3-array" 的方式重导出 d3-array 的全部 API,因此上述函数在 d3 主包中均以 d3. 前缀直接可用;package.json 中声明的依赖为 d3-array: ^3.2.4。test/d3-test.js 中的测试还保证了 d3 主包重导出每个子模块(包括 d3-array)的每一个导出,因此 d3.ticks、d3.nice 等入口与子模块一一对应。
d3.ticks(start, stop, count)
返回一个数组,包含 start 与 stop 之间(含两端,视情况)大约 count + 1 个等距、整齐取整的值。每个刻度值都是 10 的某次幂乘以 1、2 或 5:
d3.ticks(1, 9, 5) // [2, 4, 6, 8]
d3.ticks(1, 9, 20) // [1, 1.5, 2, 2.5, 3, 3.5, 4, 4.5, 5, 5.5, 6, 6.5, 7, 7.5, 8, 8.5, 9]
两个要点值得注意:
- 数量是"约 count + 1"。d3 不保证恰好 count + 1 个值,而是在"整齐步长"与"期望密度"之间取平衡:第一个例子 count 为 5 只返回 4 个值,第二个例子 count 为 20 返回 17 个值。
- 端点是条件包含的。原文档的表述是:ticks 是 inclusive 的,即"仅当 start 和 stop 本身是与推断出的 tickStep 一致的精确整齐值时,才可能包含在结果中"。更严格地说,每个返回的刻度 t 都满足 start ≤ t 且 t ≤ stop。在上例中
1和9恰好是 0.5 步长的整齐刻度,所以都被包含;若端点是 1.2、9.7 这类值,则不会出现在结果里。
d3.tickStep(start, stop, count):先看步长
d3.tickStep 返回"若用相同参数调用 d3.ticks,相邻刻度值之间的差",即一个 10 的幂乘以 1、2 或 5 的整齐值:
d3.tickStep(1, 9, 5) // 2
如果 stop 小于 start,可以返回负的刻度步长,表示刻度递减:
d3.tickStep(9, 1, 5) // -2
文档特别提醒:受 IEEE 754 浮点精度限制,返回的步长可能不是精确的十进制小数(例如看似 0.3 的值实际是 0.30000000000000004)。展示给人看之前,应使用 d3-format 进行格式化取整显示。
d3.tickIncrement(start, stop, count):为浮点精度而生的整数步长
d3.tickIncrement 与 d3.tickStep 类似,但有两个关键差异(以官方文档表述为准):
- 要求 start 始终小于等于 stop(不允许递减区间);
- 若按给定 start、stop、count 计算出的刻度步长小于 1,则返回负逆步长(negative inverse tick step)。
d3.tickIncrement(1, 9, 5) // 2
d3.tickIncrement(1, 9, 20) // -2, meaning a tick step 0.5
第二条示例是理解这个 API 的钥匙:步长 0.5 本身是小数,浮点加法 t += 0.5 在多次累加后可能漂移出整齐值(比如产生 3.5000000000000004)。tickIncrement 返回 -2 意味着"步长 = 1/2"——d3.ticks 内部可以用整数 2 做除法/缩放计算,把累加漂移降到最低。文档原文指出:此方法保证总是返回整数,并被 d3.ticks 用来保证返回的刻度值在 IEEE 754 浮点中被尽可能精确地表示。
d3.nice(start, stop, count):把区间扩成整齐边界
d3.nice 返回一个新区间 [niceStart, niceStop],它覆盖给定区间 [start, stop],且 niceStart 和 niceStop 保证与对应的 刻度步长 对齐:
d3.nice(1, 9, 5) // [0, 10]
即原本 [1, 9] 的区间被向外扩成 [0, 10],两端都落在 2 的整数倍上,坐标轴刻度从 0 和 10 起步,视觉上更整洁。与 d3.tickIncrement 相同,此方法也要求 start 小于等于 stop。
d3.range(start, stop, step):等差数列生成器
d3.range 返回一个等差数列数组,行为类似 Python 内置 range(原文档以该文档表述为准,此处不给出外部链接,语义即:起点、终点(不含)、步长)。它常用于遍历等距数值序列,例如数组索引或线性比例的固定刻度:
d3.range(6) // [0, 1, 2, 3, 4, 5]
参数规则(完整继承自官方文档):
- step 省略时默认为 1;start 省略时默认为 0;
- stop 是开区间端点,不会出现在结果中;
- step 为正时,最后一个元素是小于 stop 的最大 start + i * step;step 为负时,最后一个元素是大于 stop 的最小 start + i * step。
d3.range(5, -1, -1) // [5, 4, 3, 2, 1, 0]
无限区间返回空数组:若返回数组将包含无穷多个值,则返回空 range:
d3.range(Infinity) // []
参数不必是整数,但整数结果更可预期。返回数组中的值定义为 start + i * step,其中 i 从 0 到"数组总长度减一"的整数。注意这是"按下标乘步长"的定义,而非逐步累加:
d3.range(0, 1, 0.2) // [0, 0.2, 0.4, 0.6000000000000001, 0.8]
这个 0.6000000000000001 来自 IEEE 754 双精度浮点(0.2 × 3 的精确结果就是 0.6000000000000001)。展示给人前应使用 d3-format 做合适的舍入格式化;也参见 d3-scale 中 linear.tickFormat。
需要固定长度时,用整数 range 加 map。原文档给出了一个反模式对照:
d3.range(0, 1, 1 / 49) // 👎 returns 50 elements!
d3.range(49).map((d) => d / 49) // 👍 returns 49 elements
直接写 1/49 作步长会得到 50 个元素(浮点误差让端点判定"多走了一步");先用整数 d3.range(49) 再映射,长度精确可控。
与 d3-scale、d3-axis 的配合
这组 ticks 函数不是孤立存在的,它们是 d3 刻度生态的底层原语:
- d3-scale 的 linear.ticks 在计算线性比例尺刻度时,正是基于
d3.ticks这类函数对域(domain)求代表值; - d3-scale 的 linear.tickFormat 与 linear.nice 分别是"d3.tickFormat / d3.nice 的比例尺版"——
*linear*.nice(count)内部同样调用d3.nice把域扩成整齐边界; - d3-axis 坐标轴 的
*axis*.ticks(count)、*axis*.tickArguments()等方法(见 docs/api.md 的 API 总览)最终也是把 count 传给这套刻度机制。
典型工作流是:用 *linear*.ticks(count)(或直接用 d3.ticks)取刻度位置,用 *linear*.tickFormat(count) 生成与之配套的标签格式(精度由 d3.tickStep 推断的步长决定),必要时先对域调用 *linear*.nice() 让坐标轴起止点更整齐。
实践要点小结
d3.ticks(start, stop, count)返回约 count + 1 个整齐等距刻度,端点仅在恰好整齐时包含;d3.tickStep给出整齐步长(可为负表示递减),d3.tickIncrement给出整数化的步长表示(步长小于 1 时返回负逆步长),专供浮点精度敏感的内部计算;d3.nice(start, stop, count)把区间外扩到与步长对齐的整齐边界;d3.range生成等差数列,stop 为开区间,步长可为负,无限序列返回空数组;- 涉及小数的输出(如
0.6000000000000001)在展示前用 d3-format 格式化;需要精确长度的序列优先d3.range(n).map(...)的整数映射写法。
相关文档入口:d3-array 刻度 API 原文档、d3-scale 线性比例尺、d3-format 数字格式化、docs/api.md 全量 API 索引。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00