Thinking with Joins

本文深入解析D3.js中数据关联(join)的概念,通过实例演示如何利用数据关联来创建SVG元素,并阐述其在数据可视化中的应用。文章详细解释了数据关联的三个关键状态:进入(enter)、更新(update)和退出(exit),并展示如何通过简单的代码实现动态数据可视化。此外,文章还介绍了如何通过数据关联进行元素操作,如添加、更新和移除元素,以实现平滑的数据集变化过渡。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

点击打开链接 



February 5, 2012Mike Bostock

Thinking with Joins

Say you’re making a basic scatterplot using D3, and you need to create some SVG circle elements to visualize your data. You may be surprised to discover that D3 has no primitive for creating multiple DOM elements. Wait, WAT?

Sure, there’s the append method, which you can use to create a single element.

Here  svg refers to a single-element selection containing an <svg> element created previously (or selected from the current page, say).
svg.append("circle")
    .attr("cx", d.x)
    .attr("cy", d.y)
    .attr("r", 2.5);

But that’s just a single circle, and you want many circles: one for each data point. Before you bust out a for loop and brute-force it, consider this mystifying sequence from one of D3’s examples.

Here  data is an array of JSON objects with  x and  y properties, such as:  [{"x"1.0"y":1.1}, {"x"2.0"y":2.5}, …].
svg.selectAll("circle")
    .data(data)
  .enter().append("circle")
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; })
    .attr("r", 2.5);

This code does exactly what you need: it creates a circle element for each data point, using the x andy data properties for positioning. But what’s with the selectAll("circle")? Why do you have to select elements that you know don’t exist in order to create new ones? WAT.

Here’s the deal. Instead of telling D3 how to do something, tell D3 what you want. You want the circle elements to correspond to data. You want one circle per datum. Instead of instructing D3 to create circles, then, tell D3 that the selection "circle" should correspond to data. This concept is called the data join:

DataEnterUpdateElementsExit

Data points joined to existing elements produce the update (inner) selection. Leftover unbound data produce the enter selection (left), which represents missing elements. Likewise, any remaining unbound elements produce the exit selection (right), which represents elements to be removed.

Now we can unravel the mysterious enter-append sequence through the data join:

  1. First, svg.selectAll("circle") returns a new empty selection, since the SVG container was empty. The parent node of this selection is the SVG container.

  2. This selection is then joined to an array of data, resulting in three new selections that represent the three possible states: enterupdate, and exit. Since the selection was empty, the update and exit selections are empty, while the enter selection contains a placeholder for each new datum.

  3. The update selection is returned by selection.data, while the enter and exit selections hang off the update selection; selection.enter thus returns the enter selection.

  4. The missing elements are added to the SVG container by calling selection.append on the enter selection. This appends a new circle for each data point to the SVG container.

Thinking with joins means declaring a relationship between a selection (such as "circle") and data, and then implementing this relationship through the three enterupdate and exit states.

But why all the trouble? Why not just a primitive to create multiple elements? The beauty of the data join is that it generalizes. While the above code only handles the enter selection, which is sufficient for static visualizations, you can extend it to support dynamic visualizations with only minor modifications for update and exit. And that means you can visualize realtime data, allow interactive exploration, and transition smoothly between datasets!

Here’s an example of handling all three states:

var circle = svg.selectAll("circle")
    .data(data);

circle.exit().remove();

circle.enter().append("circle")
    .attr("r", 2.5);

circle
    .attr("cx", function(d) { return d.x; })
    .attr("cy", function(d) { return d.y; });
To control how data is assign­ed to elements, you can pro­vide a key function.

Whenever this code is run, it recomputes the data join and maintains the desired correspondence between elements and data. If the new dataset is smaller than the old one, the surplus elements end up in the exit selection and get removed. If the new dataset is larger, the surplus data ends up in theenter selection and new nodes are added. If the new dataset is exactly the same size, then all the elements are simply updated with new positions, and no elements are added or removed.

Thinking with joins means your code is more declarative: you handle these three states without any branching (if) or iteration (for). Instead you describe how elements should correspond to data. If a given enterupdate or exit selection happens to be empty, the corresponding code is a no-op.

Joins also let you target operations to specific states, if needed. For example, you can set constant attributes (such as the circle’s radius, defined by the "r" attribute) on enter rather than update. By reselecting elements and minimizing DOM changes, you vastly improve rendering performance! Similarly, you can target animated transitions to specific states. For example, for entering circles to expand-in:

circle.enter().append("circle")
    .attr("r", 0)
  .transition()
    .attr("r", 2.5);

Likewise, to shrink-out:

circle.exit().transition()
    .attr("r", 0)
    .remove();

Now you’re thinking with joins!

Comments or questions? Discuss on HN.

Addendum

I’ve written a series of examples on the general update pattern as a followup to this post.

February 5, 2012 Mike Bostock
电力系统潮流计算是电力工程领域的一项核心技术,主要用于分析电力网络在稳态运行条件下的电压、电流、功率分布等运行状态。MATLAB凭借其强大的数值计算功能和便捷的编程环境,成为电力系统潮流计算的重要工具,它提供了丰富的数学函数库,能够高效地处理复杂的电力系统计算任务。 本压缩包中的“潮流计算MATLAB程序”是一套完整的电力系统潮流计算解决方案,主要包括以下几个关键部分: 数据输入模块:该模块负责读取电力系统的网络数据,包括发电机、线路、变压器等设备的参数。这些数据通常来源于IEEE测试系统或实际电网,并以特定格式存储。 网络建模:基于输入数据,程序构建电力系统的数学模型,主要涉及节点功率平衡方程的建立。每个节点的注入功率等于其消耗功率,对于发电机节点还需考虑其有功和无功功率的调节能力。 迭代算法:潮流计算的核心是求解非线性方程组,常见的算法有牛顿-拉夫森法和高斯-塞德尔法。MATLAB的优化工具箱可辅助实现这些算法,通过迭代更新节点电压和支路电流,直至满足收敛条件。 结果输出:计算完成后,程序能够输出关键性能指标,如节点电压幅值和相角、支路功率流、发电机的有功无功功率等。这些信息对于分析电网运行状态和制定调度策略具有重要意义。 可视化功能:程序可能包含图形用户界面(GUI),用于展示计算结果,例如绘制网络拓扑图并标注节点电压和支路功率,便于用户直观理解计算结果。 错误处理与调试:良好的程序设计应包含错误检测和处理机制,以应对不合理数据或计算过程中出现的问题,并给出适当的提示。 对于电力系统分析课程的学生来说,这个MATLAB程序是一个宝贵的学习资源。它不仅有助于学生掌握电力系统的理论知识,还能让他们了解如何将理论应用于实践,通过MATLAB解决实际问题。尽管该程序是作者一周内完成的,可能存在一些未完善之处,但使用者可以在参考的基础上逐步改进和完善,使其更贴合自身需求。 总之
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值