
d3 密度估计详解contourDensity 二维核密度估计与等高线绘制【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本文围绕 d3 仓库中的密度估计文档 density.md 展开系统讲解d3.contourDensity()的完整 API——包括坐标访问器、单元格大小、阈值生成、高斯核带宽等全部配置项——并说明密度估计结果的 GeoJSON 输出如何经由d3.geoPath渲染。读完后你将能够独立实现一个可避免大数据集过度重叠overplotting的密度等高线图并理解 d3-contour 从点云到等高线多边形的底层计算链路。什么是密度估计为什么需要等高线当数据集包含数万乃至数百万个点时直接绘制散点图会导致严重的过度重叠点的颜色互相覆盖真实的数据分布反而被掩盖。密度等高线通过估计点云在各处的单位面积点数number of points per square pixel用一圈圈嵌套的等高线区域来呈现密度分布——内圈密度高、外圈密度低——从而在一张图中同时表达位置关系与密集程度。d3 通过contourDensity方法实现了快速的两维核密度估计two-dimensional kernel density estimation。其典型应用场景包括Old Faithful 热泉的喷发时长与间隔时长的关系散点密度图53,940 颗钻石的重量与价格关系密度图——这个量级的散点用普通 scatterplot 几乎无法分辨而密度等高线清晰呈现出价格随重量递增的带状分布。密度估计是 d3-contour 模块的两大能力之一另一个是面向数值网格的通用等高线生成器 contours。两者共享同一套 marching squares 算法与 GeoJSON 输出格式区别在于contours直接接收一个数值网格而contourDensity接收原始点云先自行把点分箱binning到网格上再做核平滑最后再走同样的等高线提取流程。在 d3 仓库中的位置contourDensity并非本仓库源码直接实现而是来自上游d3-contour包的再导出。可以从仓库结构确认这一事实链package.json 声明依赖d3-contour: ^4.0.2且发布版本为 d3 7.9.0src/index.js 中export * from d3-contour;将contourDensity、contours等 API 原样提升到顶层d3命名空间test/d3-test.js 逐模块校验 “d3 exports everything from d3-contour”确保任何 d3-contour 的导出都能从d3上直接访问docs/api.md 的 API 索引中列出了d3.contourDensity及*density*.x / y / weight / size / cellSize / thresholds / bandwidth / contours共 9 个条目即下文将逐一讲解的完整方法面。contourDensity()构造密度估计器contourDensity()以默认设置构造一个新的密度估计器返回一个可直接调用、也可继续链式配置的对象const density d3.contourDensity() .x(d d[0]) .y(d d[1]) .size([width, height]) .thresholds(20);density(data)计算并返回 GeoJSON 等高线调用density(data)时对给定的数据数组data估计密度返回一个 GeoJSON MultiPolygon 几何对象数组该链接为文档原文引用的外部规范仅作格式说明。关键语义每个几何对象代表“估计密度 ≥ 对应阈值”的区域。第 i 个几何对象对应第 i 个阈值每个几何对象自身的阈值暴露为geometry.value属性可直接用于填充色阶映射坐标来源每个数据点的 x、y 坐标分别由density.x与density.y访问器计算权重density.weight指定每个数据点的相对贡献默认均为 1精度边界生成的等高线只在估计器定义的size范围内准确。返回的 MultiPolygon 通常直接交给 d3.geoPath 渲染投影使用null或 geoIdentity因为密度结果本身就是像素平面坐标不需要地理投影。一个最小渲染示例import {contourDensity, geoPath, scaleSequential, interpolateViridis} from d3; const density contourDensity() .x(d xScale(d.weight)) .y(d yScale(d.price)) .size([width, height]) .thresholds(20); const polygons density(diamonds); const svg ...; svg.append(g) .selectAll(path) .data(polygons) .join(path) .attr(d, geoPath(null)) // null 投影 恒等变换 .attr(fill, d color(d.value)); // 用 geometry.value 驱动色阶density.x(x) 与 density.y(y)坐标访问器若指定参数x/y则设置坐标访问器否则返回当前访问器。二者默认值分别是function x(d) { return d[0]; }function y(d) { return d[1]; }也就是说默认情况下数据点被假定为[x, y]二元组数组。若数据是对象如{weight, price}必须像上例那样显式设置访问器。访问器在每次调用density(data)时对每个点求值因此可以依赖闭包中的比例尺把原始数据单位转换为像素单位。density.weight(weight)点权重weight访问器决定每个数据点在密度场中的相对贡献默认实现为function weight() { return 1; }即每个点贡献相同。当数据点本身携带强度信息如样本置信度、事件计数、金额时可写成.weight(d d.count)等高线将反映加权后的密度而非简单点数。density.size(size)估计器范围size指定为[width, height]形式的数组其中width是 x 方向最大值、height是 y 方向最大值通常与 SVG/Canvas 的绘图区域尺寸一致。未指定时返回当前 size默认值为[960, 500]。再次强调密度估计结果只在该 size 范围内有效超出该范围的数据点不参与计算渲染时也应把等高线裁剪到同一边界内。density.cellSize(cellSize)网格粒度cellSize设置底部分箱网格中单个单元格的大小取值必须是正整数未指定时默认值为 4。两个关键细节单元格大小会向下取整到最近的 2 的幂如传入 5 实际生效为 4传入 10 生效为 8——这一设计让核平滑可以借助位运算在 2 的幂大小的块上快速进行更小的 cell 产生更精细的等高线多边形但计算成本更高。从这一行为可以推断实现上存在明显的精度/速度权衡旋钮数据点分布平滑时默认的 4 已经足够当需要分辨细长的低密度丝状结构时可下调到 2 或 1代价是计算时间上升。density.thresholds(thresholds)阈值生成若指定thresholds设置阈值生成器可以是函数或数组否则返回当前生成器默认生成约二十个整齐取整的密度阈值。阈值数组的语义是 [x0, x1, …]第一条生成的密度等高线对应“估计密度 ≥ x0”的区域第二条对应“估计密度 ≥ x1”的区域依此类推——每个阈值值恰好对应一个生成的 MultiPolygon 几何对象阈值值即geometry.value第一个值x0通常应大于零否则最外层等高线会铺满整个 size 区域。若传入的是数字count而非数组则利用 d3.ticks 生成大约count个均匀分布的整齐阈值。阈值数量决定了等高线的“圈数”与渲染路径数量20 个阈值即 20 条 MultiPolygon逐层填充即可得到经典的嵌套色带效果。density.bandwidth(bandwidth)高斯核带宽bandwidth设置高斯核的带宽标准差。未指定时返回当前值默认值约为 20.4939…即 √420对应一组预计算高斯核的尺度。两个约束指定值必须非负指定值会被四舍五入到该实现所支持的最接近的离散值——这与cellSize取 2 的幂是同一类工程取舍d3-contour 预先计算有限组高斯核系数用量化后的带宽换取快速查表式的卷积而非逐点计算连续高斯函数。带宽是密度图最重要的形态参数过大会抹平聚集结构、让峰消失过小则每个点各自成峰视觉上退化为模糊的散点。实践中建议以像素为单位取明显小于数据簇间距的值默认 20 左右适合 960×500 的典型画布。density.contours(data)复用网格任意阈值切片density.contours(data)返回一个contour(value)函数可在已给定数据上计算任意单阈值等高线而无需重算底层网格适合交互式场景拖动滑块实时改变阈值。返回的contour函数还暴露contour.max属性表示网格上的最大密度值可用作阈值滑块的上限或色阶的 domain 上限const contour density.contours(diamonds); // 任意时刻 const polygon contour(contour.max / 2); // 密度达到峰值一半以上的区域这与 contours.md 中contours.contour(values, threshold)的单阈值计算相对应只是输入从显式网格换成了点云。与通用等高线生成器 contours() 的关系理解contourDensity最快的方式是把它看作三段流水线的组合分箱按size与cellSize把点云累加到网格weight参与累加核平滑以bandwidth指定的高斯核在网格上卷积得到平滑密度场等高线提取对密度场应用 marching squares对每个thresholds阈值输出一个 MultiPolygon。而 docs/d3-contour/contour.md 描述的d3.contours()直接跳过了前两步接收现成的数值网格如 GeoTIFF 解码的地表温度、图像像素值做第 3 步。两者的输出格式一致GeoJSON MultiPolygon geometry.value因此下游的geoPath渲染代码完全通用。marching squares 这一点也在 d3-contour 模块总览 中被明确说明“This module computes contour polygons by applying marching squares to a rectangular grid of numeric values.”默认值速查方法默认值说明density.xd d[0]x 坐标访问器density.yd d[1]y 坐标访问器density.weight() 1点权重density.size[960, 500]估计器像素范围结果仅在其内准确density.cellSize4分箱网格单元格大小向下取整到 2 的幂density.thresholds约 20 个整齐阈值每个阈值对应一个 MultiPolygondensity.bandwidth≈ 20.4939…高斯核标准差量化到实现支持的最接近值必须非负小结d3.contourDensity()用“分箱 高斯核平滑 marching squares”三步把点云转化为密度等高线API 面共 9 个条目构造器、density(data)调用以及x/y/weight/size/cellSize/thresholds/bandwidth/contours八个配置方法输出统一的 GeoJSON MultiPolygon 并以geometry.value携带阈值。通过 src/index.js 的再导出与 package.json 中d3-contour ^4.0.2的依赖声明可以确认本文所述行为适用于 d3 7.x 系列test/d3-test.js 则保证了这些 API 从顶层d3命名空间可稳定访问。对超过数万点的散点数据密度等高线是在不丢失分布形态的前提下避免过度重叠的首选可视化手段。【免费下载链接】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),仅供参考