资讯动态

Vega Zoomable Binned Plot:基于信号驱动的平移、缩放与 Stable Bins 分箱密度图实战解析

发布时间:2026/9/23 9:59:52 来源:尧图企业网站定制
数据可视化【免费下载链接】vegaA visualization grammar.项目地址https://gitcode.com/gh_mirrors/ve/vega点击查看免费下载本篇文章以 Vega 官方示例zoomable-binned-plot可缩放分箱散点图为研究对象完整拆解其 VEGA JSON 规格中点击拖拽平移、滚轮缩放、分箱聚合密度着色、Stable Bins 开关等核心机制。读者阅读本文后将能够理解 Vega 信号Signal驱动的交互式坐标域domain联动原理掌握bin、extent、aggregate等数据变换的搭配方式并能据此构建属于自己的可缩放分箱可视化。文中所有证据均来自本仓库vega的 示例规格、示例说明 及 vega-transforms 与 vega-statistics 源码。示例概述它在演示什么根据 示例说明文档 的描述这是一个支持平移与缩放的分箱聚合散点图点击并拖拽鼠标可以平移视野滚动滚轮可以缩放。它的技术演示重点有两个分箱binning如何工作——原始 2002 个二维数据点被离散化到一个个矩形分箱中以箱内点计数作为颜色编码的密度估计关闭 stable bins 选项后分箱对齐bin alignment的变化如何影响感知到的密度分布——这是本示例最微妙、也最具教学价值的一点。从仓库的 示例规格文件 可以看到整个可视化由以下部分构成信号signalsstable、hover、down、delta、anchor、zoom、xdom、ydom、size等负责全部交互状态数据datapoints原始点与density分箱聚合后的密度数据缩放scalesxscale、yscale的 domain 直接绑定到xdom/ydom信号随平移缩放实时变化标记marksrect绘制分箱矩形symbol绘制原始散点二者均设置clip: true以裁剪视野外的图形。下面按照信号 → 数据 → 缩放/轴 → 标记的脉络逐层拆解。信号系统平移与缩放的交互引擎Vega 的交互全部建立在信号Signal之上。本示例没有使用任何lex式的现成缩放组件而是手工实现了 pan/zoom 的完整数学逻辑非常适合作为学习信号级交互的范本。基础布局信号{name: margin, value: 20}, {name: xoffset, update: -(height padding.bottom)}, {name: yoffset, update: -(width padding.left)}, {name: xrange, update: [0, width]}, {name: yrange, update: [height, 0]}xoffset/yoffset用于把坐标轴从绘图区搬到画布底部和左侧配合padding留出的空间xrange/yrange定义了 x、y 缩放的比例range范围其中yrange采用[height, 0]反向区间保证数据坐标系的 y 轴向上。平移down / xcur / ycur / delta平移的核心是记录按下时的坐标域快照再根据鼠标位移增量计算新坐标域{name: down, value: null, on: [ {events: touchend, update: null}, {events: pointerdown, touchstart, update: xy()} ]}, {name: xcur, value: null, on: [ {events: pointerdown, touchstart, touchend, update: slice(xdom)} ]}, {name: ycur, value: null, on: [ {events: pointerdown, touchstart, touchend, update: slice(ydom)} ]}, {name: delta, value: [0, 0], on: [ {events: [{source: window, type: pointermove, consume: true, between: [{type: pointerdown}, {source: window, type: pointerup}]}, {type: touchmove, consume: true, filter: event.touches.length 1}], update: down ? [down[0]-x(), y()-down[1]] : [0,0]} ]}down在按下时记录鼠标/触摸点的屏幕坐标xy()在touchend时复位为nullxcur/ycur在按下时把当前xdom/ydom复制一份作为平移基准delta在从 pointerdown 到 pointerup 之间的pointermove或单指touchmove期间持续计算鼠标位移。注意between事件过滤器限定了拖拽窗口且事件带consume: true以避免与其他交互冲突。随后xdom/ydom监听delta信号把基准域按位移比例平移{name: xdom, update: slice(xext), on: [ {events: {signal: delta}, update: [xcur[0] span(xcur) * delta[0] / width, xcur[1] span(xcur) * delta[0] / width]}, ... ]}表达式span(xcur) * delta[0] / width将像素位移换算成数据域位移——鼠标向右拖delta[0]像素坐标域整体右移对应的数据量视觉上就是画布内容跟随鼠标移动。缩放anchor / zoom / dist1 / dist2缩放围绕锚点anchor展开滚轮缩放时以鼠标所在位置的数据坐标为锚点双指捏合时以两指中点对应的数据坐标为锚点{name: anchor, value: [0, 0], on: [ {events: wheel, update: [invert(xscale, x()), invert(yscale, y())]}, {events: {type: touchstart, filter: event.touches.length2}, update: [(xdom[0] xdom[1]) / 2, (ydom[0] ydom[1]) / 2]} ]}, {name: zoom, value: 1, on: [ {events: wheel!, force: true, update: pow(1.001, event.deltaY * pow(16, event.deltaMode))}, {events: {signal: dist2}, force: true, update: dist1 / dist2} ]}invert(xscale, x())通过比例尺的反函数把鼠标像素坐标还原为数据坐标这正是缩放中心不跑偏的关键zoom对滚轮使用指数形式pow(1.001, event.deltaY * ...)让缩放倍数平滑且与 deltaMode行/页/像素三种滚动模式兼容双指手势通过dist1/dist2两个信号跟踪两次touchstart/touchmove时两指间的像素距离缩放倍率取dist1 / dist2实现捏合缩放。xdom/ydom再监听zoom信号围绕锚点做线性伸缩{events: {signal: zoom}, update: [anchor[0] (xdom[0] - anchor[0]) * zoom, anchor[0] (xdom[1] - anchor[0]) * zoom]}stable 与 size 信号{name: stable, value: true, bind: {input: checkbox}}, {name: size, update: clamp(20 / span(xdom), 1, 1000)}stable是绑定到复选框checkbox的输入信号默认true用于切换稳定分箱模式下文详述size根据当前 x 方向视野跨度动态计算符号点与分箱矩形的尺寸视野越窄放大越多点越小、箱越细公式20 / span(xdom)配合clamp(..., 1, 1000)限制在合理范围内。同时hover信号通过*:pointerover/pointerout/pointerdown/pointerup事件触发hover/leave/select/release编码集管理悬停与按下时的视觉状态。数据流水线extent → bin → aggregate原始数据 pointspoints数据直接加载 data/normal-2d.json其中包含 2002 条记录每条记录有u、v两个数值字段呈二维正态分布中心密集、外围稀疏是演示密度图的理想数据。其变换只有两个extent{name: points, url: data/normal-2d.json, transform: [ {type: extent, field: u, signal: xext}, {type: extent, field: v, signal: yext} ]}extent变换计算字段的最小/最大值并存入指定信号xext、yext。其实现位于 Extent.js遍历数据pulse.visit维护min/max并跳过null/NaN值if (v ! null)若最终范围为无穷大则发出警告并将 extent 置为undefined。这里得到的xext/yext是全量数据的静态范围是稳定分箱的基准。密度数据 density分箱 聚合{name: density, source: points, transform: [ {type: extent, field: u, signal: xextf}, {type: extent, field: v, signal: yextf}, {type: bin, field: u, extent: {signal: stable ? xextf : xdom}, as: [ustart, uend], maxbins: 12, nice: {signal: stable}}, {type: bin, field: v, extent: {signal: stable ? yextf : ydom}, as: [vstart, vend], maxbins: 12, nice: {signal: stable}}, {type: aggregate, groupby: [ustart, uend, vstart, vend], as: [count]} ]}这是全示例的灵魂所在注意两处bin变换的参数extent分箱范围被绑定为stable ? xextf : xdomy 轴同理。当stable为真时分箱范围固定为全量数据的 extent当stable为假时分箱范围跟随当前视野xdom/ydom动态变化。nice同样由stable信号驱动决定是否把分箱边界凑整到美观的数值上。在 vega-transforms/src/Bin.js 中Bin.Definition定义了该变换的全部参数field必填、interval默认 true输出[bin0, bin1]区间、anchor、maxbins默认 20、base默认 10、divide默认[5, 2]用于细化步长、extent必填的二元数组、span、step、steps、minstep默认 0、nice默认 true、name与as默认[bin0, bin1]。本示例显式设置了maxbins: 12、as: [ustart, uend]以及[vstart, vend]并省略了其余参数以使用默认值。底层步长计算位于 vega-statistics/src/bin.js当未显式给出step时会根据span取值范围跨度、maxbins与base计算一个好看的步长随后在nice为真时把min向下取整、max向上取整Math.floor(min / step) * step/Math.ceil(max / step) * step使分箱边界落在整齐的刻度上。这正是stable关闭时分箱对齐变化的根源——一旦分箱范围跟随视野变化步长与边界随之重算同一批数据点会被划分到不同的箱中。最后aggregate以四个箱边界字段分组输出每箱的点数count作为密度着色的依据。为什么 stable bins 会影响感知密度理解stable的意义需要回到可视化原理分箱密度估计的结果强烈依赖分箱的对齐方式bin alignment。开启 stable默认分箱的extent固定为全量数据范围xextf/yextfnice: true使边界对齐到整数刻度如 -0.5、-0.4、...、0.5。此时无论你如何平移缩放分箱网格在数据空间中保持不动密度颜色不会因视野改变而闪烁或漂移——这正是稳定的含义。关闭 stable分箱extent改为当前视野xdom/ydomnice同时变为false。每次缩放或平移分箱网格都会随视野重新对齐。放大到局部时步长变小、网格变密但边界落在非整数位置于是同一区域在不同缩放级别下会被切成不同的箱密度颜色的分布也随之变化可能造成这里密那里疏的视觉错觉。换句话说该示例用同一个复选框向读者直观展示了分箱统计中一个经典陷阱密度图的视觉结果不仅取决于数据还取决于分箱网格的放置方式。稳定分箱保证了视野变换下密度估计的一致性代价是放大后分箱较粗非稳定分箱在放大时能获得更精细的网格但会引入对齐效应。此机制正是 docs/examples/zoomable-binned-plot.md 中所说的 how changes to the bin alignment can affect the perceived density。缩放与渲染从信号到画面动态坐标域的比例尺xscale/yscale的 domain 直接引用xdom/ydom信号因此平移缩放信号更新时比例尺随之变化所有依赖它们的标记、坐标轴自动重绘{name: xscale, zero: false, domain: {signal: xdom}, range: {signal: xrange}}, {name: yscale, zero: false, domain: {signal: ydom}, range: {signal: yrange}}zero: false表明坐标域不强制包含 0因为分箱密度图关心的是数据范围本身。颜色比例尺则取分箱计数count的线性映射使用内置的heatmap颜色方案并以 HCL 色彩空间插值interpolate: hcl以获得感知均匀的密度渐变。坐标轴与标记坐标轴位于画布外侧orient: top/right通过xoffset/yoffset信号平移到绘图区之外同时config.axis去掉了轴线domain: false、缩短刻度线tickSize: 3并使用等宽字体保证缩放过程中刻度标签清晰可读config: {axis: {domain: false, tickSize: 3, tickColor: #888, labelFont: Monaco, Courier New}}两个标记都设置了clip: true避免缩放后视野外的图形溢出rect 标记从density数据取每个箱的ustart/uend、vstart/vend映射到 x/y 位置填充色由color比例尺按count决定size信号在放大时让箱间留出间距呈现出马赛克密度网格symbol 标记叠加绘制全部原始散点黑色、fillOpacity: 0.6让读者在分箱密度的同时仍能看到底层数据分布缩放放大后点会随size信号缩小避免完全遮盖分箱颜色。如何运行与调试该示例仓库的 docs/examples 目录中每个示例都配有.md说明与.vg.json规格。该示例的运行与调试方式如下本地构建文档站点在仓库根目录安装依赖后按 docs/Gemfile 与 docs/_config.yml 的配置运行 Jekyll 站点示例页通过 _includes/example 与 _includes/embed 模板将规格内联渲染成可交互的 Vega View默认 SVG 渲染器并提供查看源码 / 导出 PNG / 导出 SVG的入口。单独加载规格直接用任意 Vega 渲染环境如vega.parsevega.View见 packages/vega加载 zoomable-binned-plot.vg.json即可获得完整的平移缩放交互无需额外代码。交互验证要点勾选/取消勾选stable bins复选框后重复平移与缩放观察分箱网格与密度颜色的变化体会分箱对齐效应鼠标滚轮缩放时观察缩放中心是否锁定在鼠标位置验证anchor的invert逻辑放大到边缘时观察size信号如何使符号点与分箱矩形缩小clamp(20 / span(xdom), 1, 1000)以及clip: true如何裁剪视野外图形。总结zoomable-binned-plot是一个小而全的 Vega 交互范例它把信号、事件、数据变换、比例尺与标记串联成一个自洽的系统交互down/xcur/ycur/delta实现拖拽平移anchor/zoom/dist1/dist2实现滚轮与捏合缩放xdom/ydom作为坐标域信号统一驱动比例尺统计extent提供静态范围binBin.js、bin.js负责离散化与对齐aggregate计算箱内计数教学价值stable复选框把分箱对齐如何影响感知密度这一统计可视化中的经典问题转化为一个可实时操作的直观演示。对于想要在自己项目中实现可缩放密度图的开发者本示例提供了完整的参考实现对于想深入理解 Vega 信号系统与bin变换的读者它则是一份可以直接对照源码研读的活教材。赞分享数据可视化【免费下载链接】vegaA visualization grammar.项目地址https://gitcode.com/gh_mirrors/ve/vega点击查看免费下载相关推荐如何永久备份微信聊天记录WeChatExporter开源工具终极指南如何永久备份微信聊天记录WeChatExporter开源工具终极指南 你是否曾因手机丢失或系统升级而丢失珍贵的微信聊天记录那些与家人的温馨对话、工作的重要信数据可视化Vega 可缩放圆形打包图Zoomable Circle Packing实战信号驱动的钻取、补间与淡入淡出动画Vega 可缩放圆形打包图Zoomable Circle Packing实战信号驱动的钻取、补间与淡入淡出动画 本文以 Vega 官方示例 zoomabl数据可视化gVisor Systrap 平台深度解析基于 Seccomp SIGSYS 信号陷入的沙箱线程实现gVisor Systrap 平台深度解析基于 Seccomp SIGSYS 信号陷入的沙箱线程实现 本文以 gVisor 仓库中 systrap 平台说明文云原生容器运行时操作系统应用安全上一篇终极指南bililive-go 录制策略详解与文件分割最佳实践下一篇构建企业级打印代理网络vue-plugin-hiprint中转服务架构深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价