D3 数据分组实战:d3.group、rollup 与 index 分组聚合 API 完全指南

发布时间:2026/9/7 17:40:33
D3 数据分组实战:d3.group、rollup 与 index 分组聚合 API 完全指南 D3 数据分组实战d3.group、rollup 与 index 分组聚合 API 完全指南【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本篇指南围绕 d3-array 提供的分组聚合 APId3.group、d3.groups、d3.rollup、d3.rollups、d3.index、d3.indexes、d3.flatGroup、d3.flatRollup、d3.groupSort完整讲解其签名、嵌套分组语义、返回值顺序与实现细节。读完后你将掌握在 D3 7 数据流中做分组、计数、索引和按组排序的全部标准手法并能理解这些函数底层为何能支持 Date 等非原始类型作为键。分组 API 在 d3 生态中的位置在 D3 7 中分组Grouping是 d3-array 模块的核心能力之一官方文档目录将其概括为 Group discrete values对离散值分组入口见 d3-array 文档总览。当前仓库为 d3 元包package.json 中声明版本 7.9.0并依赖d3-array: ^3.2.4入口文件 通过export * from d3-array将 d3-array 的全部导出提升到d3命名空间下因此上述所有分组函数都可以直接以d3.group(...)的形式调用。这一命名空间完整透传并非口头约定而是有测试保障的测试文件 遍历package.json中的每个依赖模块断言其所有导出除version外都存在于d3对象中。也就是说group、rollup、index等函数出现在d3命名空间是仓库级测试验证过的事实。适用前提d3 7 是纯 ES Modules 分发要求 Node.js 12 及以上见 变更日志 与 package.json 的engines字段。此外变更日志 明确记录了 D3 6.0 引入d3.group与d3.rollup作为替代旧版d3.nest的强大聚合函数——如果你维护的是 d3 5 时代代码迁移时可以用本文的 API 对照表直接替换。d3.group分组的基础形式d3.group(*iterable*, ...*keys*)将指定的可迭代对象数组、Map、Set 等按一个或多个键函数分组返回一个从key到元素数组的 InternMap。以官方文档中常用的 penguins企鹅示例数据集为例按species字段分组const species d3.group(penguins, (d) d.species);然后即可取出Adelie组的全部元素species.get(Adelie) // Array(152)关键点多键产生嵌套 Map如果指定了多个键返回的是嵌套 InternMap。例如按物种和性别两级分组const speciesSex d3.group(penguins, (d) d.species, (d) d.sex)取值时需要链式getspeciesSex.get(Adelie).get(FEMALE) // Array(73)键的顺序确定分组结果中元素的顺序是每个 key 首次出现的顺序elements are returned in the order of the first instance of each key。这意味着对同构输入group的输出顺序是稳定可预期的适合直接驱动图表的分组轴序而不必二次排序。第一参是任何 iterable不仅是数组NodeList、Map 的 values、generator 等都可直接传入无需先Array.from。d3.group的实现位于依赖包 d3-array 的src/group.js本仓库 package.json 声明的d3-array依赖官方文档 group.md 的 Source 指向即该文件本文其余 rollup 系函数均出自同一实现模块。d3.groups返回数组而非 Map 的分组形式d3.groups(*iterable*, ...*keys*)与group语义等价区别仅在于返回普通数组形式的[*key*, *value*]条目列表const species d3.groups(penguins, (d) d.species); // [[Adelie, Array(152)], …]若指定多个键每个value也是嵌套的[key, value]条目数组。元素同样按每个 key 首次出现的顺序返回。选型建议从两者差异推断需要按键随机查找时用group返回的 Map只需要一次性遍历所有分组比如为图例、分组轴生成有序条目时groups返回的数组可以直接for...of解构更贴近数组编程习惯也便于序列化和测试断言。d3.rollup 与 d3.rollups分组 归约d3.rollup(*iterable*, *reduce*, ...*keys*)在分组的同时对每组执行归约reduce返回从key到归约值的 InternMap。注意签名中reduce位于键函数之前——这是与group最容易被写错的位置。示例按物种分组并计数const speciesCount d3.rollup(penguins, (D) D.length, (d) d.species);取出Adelie的数量speciesCount.get(Adelie) // 152多级分组 归约同样支持嵌套 InternMap 逐层getconst speciesSexCount d3.rollup(penguins, (D) D.length, (d) d.species, (d) d.sex); speciesSexCount.get(Adelie).get(FEMALE) // 73归约函数(D) D.length只是最简形式任何 reduce 表达式都适用例如(D) d3.sum(D, (d) d.body_mass_g)求组内体重总和、(D) d3.median(D, (d) d.flippers_length_mm)求组内中位数——分组与 d3-array 的统计函数见 summarize 文档天然组合。d3.rollups(*iterable*, *reduce*, ...*keys*)是rollup的数组形式const speciesCounts d3.rollups(penguins, (D) D.length, (d) d.species); // [[Adelie, 152], …]同样按 key 首次出现顺序返回多键时每个value是嵌套的[key, value]条目数组。先 rollup 再取数组与直接 rollups结果一致后者少一层 Map 构造适合纯遍历场景。d3.index 与 d3.indexes构建唯一键索引d3.index(*iterable*, ...*keys*)本质上是用一个特殊归约器调用rollup该归约器提取每组的首个元素并且当某组出现多于一个元素时会抛出错误。因此index适用于键值唯一的数据。示例按日期索引 AAPL 股票数据const aaplDate d3.index(aapl, (d) d.Date);随后即可按日期 O(1) 取值aaplDate.get(new Date(2013-12-31)).Close // 80.145714这里有一个重要细节new Date(...)是对象而非原始值原生Map无法让两个同值但不同实例的 Date 匹配而index底层使用 InternMap因此这种按日期对象的检索可以正常工作机制见下文 InternMap 一节。使用边界index抛错的行为意味着它对脏数据重复键零容忍。如果数据可能含重复键应先用group分组并检查每组长度或改用rollup自定义冲突处理逻辑。元素返回顺序为输入顺序。d3.indexes(*iterable*, ...*keys*)与index相同但返回[key, value]条目数组。官方文档直言其用途有限This probably isnt useful for anything它存在的意义是与groups、rollups保持 API 对称性。实践中除非需要数组形式的索引结果一般直接使用index。d3.flatGroup 与 d3.flatRollup扁平化遍历多级分组多级group/rollup返回嵌套 Map遍历时需要递归深入每一层。d3.flatGroup(*iterable*, ...*keys*)提供了扁平替代形式等价于group但返回扁平数组[key0, key1, …, values]而不是嵌套 Map每个分组的完整键路径与组内元素列表并排放在同一个数组里一次for...of即可遍历所有叶子分组。d3.flatRollup(*iterable*, *reduce*, ...*keys*)同理等价于rollup但返回[key0, key1, …, value]扁平结构。两者官方定位为useful for iterating over all groups便于遍历所有分组是实现多级分组下钻、递归渲染分组柱状图、导出分组报表时的省力写法避免了手写递归解嵌套 Map 的代码。d3.groupSort按组内统计值对键排序分组之后常见的下一步是按组的某种度量排序键例如按各物种体重的中位数升序排列物种名。d3.groupSort(*iterable*, *comparator*, *key*)一次完成分组 按比较器排序 返回有序键数组三步d3.groupSort(penguins, (D) d3.median(D, (d) d.body_mass_g), (d) d.species) // [Adelie, Chinstrap, Gentoo]降序排列只需对组度量取负d3.groupSort(penguins, (D) -d3.median(D, (d) d.body_mass_g), (d) d.species) // [Gentoo, Adelie, Chinstrap]参数形态有两种accessor 形式第二参是单参函数(D) 组度量值d3 将其两两相减来比较两个组——上面的示例即此形式comparator 形式如果第二参是恰好接收两个参数的函数则把它当比较器使用它直接比较两个组a、b应返回负值表示a在前、正值表示a在后、零表示部分序。返回值只是有序的键数组不是 Map 也不是分组对象因此典型用法是把它作为d3.scaleBand的 domain让坐标轴与条形顺序跟随组内统计量排序——这是 D3 6 时代起替代d3.nest().sortKeys的标准组合。底层原理InternMap 让 Date 等非原始值可作键group、rollup、index返回的不是原生Map而是 InternMap——这是理解上述 API 能力边界的关键。intern 文档 说明了机制InternMap和InternSet分别扩展原生Map与Set原生 Map 使用 SameValueZero 算法判定键相等对象键按引用比较导致new Date(2022-01-01)与另一个同值的 Date 实例不匹配InternMap 通过键函数默认取非原始值的object.valueOfDate 会还原为时间戳数值绕过 SameValueZero使得值相等的 Date、时间等对象键能够命中同一分组——这正是d3.index(aapl, (d) d.Date)能按new Date(...)查到的原因。该设计在 D3 7 中还被推广到其他模块变更日志 记录序数比例尺ordinal scale的 domain 现在也用 InternMap 存值按valueOf而非toString去重。凡是需要以对象值做键或去重的场景都可以直接构造new d3.InternMap(entries)使用。API 速查表函数签名返回值适用场景d3.group(*iterable*, ...*keys*)嵌套 InternMap按键随机查找分组元素d3.groups(*iterable*, ...*keys*)[key, value]条目数组一次性遍历所有分组d3.rollup(*iterable*, *reduce*, ...*keys*)嵌套 InternMap值为归约结果分组计数/求和/求均值d3.rollups(*iterable*, *reduce*, ...*keys*)[key, value]条目数组遍历式输出聚合结果d3.index(*iterable*, ...*keys*)嵌套 InternMap值为首元素重复键抛错唯一键 O(1) 检索如按日期索引d3.indexes(*iterable*, ...*keys*)[key, value]条目数组与 groups/rollups 对称少用d3.flatGroup(*iterable*, ...*keys*)扁平[key0, key1, …, values]数组无递归遍历多级分组d3.flatRollup(*iterable*, *reduce*, ...*keys*)扁平[key0, key1, …, value]数组无递归遍历多级归约d3.groupSort(*iterable*, *comparator*, *key*)按组度量排序后的键数组驱动 band 比例尺 domain 排序全部函数共同遵守两条顺序约定分组结果按每个 key 首次出现的顺序排列index/indexes按输入顺序返回。键函数接收当前元素d可自由组合字段d \${d.a}-${d.b}。小结d3-array 的分组 API 用九个函数覆盖了分组、归约、索引、排序、扁平化的完整数据准备链路group/rollup产出嵌套 Map 供点查groups/rollups/flatGroup/flatRollup产出数组供遍历index处理唯一键场景groupSort解决分组排序。它们全部以 InternMap 为底座天然兼容 Date 等对象键实现集中于 d3-array 依赖包的src/group.jsgroupSort另在src/groupSort.js见 group.md 的 Source 指向并在当前仓库通过 src/index.js 的星号导出与 test/d3-test.js 的命名空间断言完整汇入d3全局 API。掌握这套 API 后D3 图表的数据预处理阶段基本不再需要手写reduce嵌套循环或依赖旧版d3.nest。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考