D3 .merge 函数
Posted
技术标签:
【中文标题】D3 .merge 函数【英文标题】:D3 .merge function 【发布时间】:2018-04-14 11:33:43 【问题描述】:尽管阅读了无数次 D3 API,但我仍在努力理解 D3 中的合并功能。
API 说:“此方法通常用于在数据连接后合并输入和更新选择。分别修改输入和更新元素后,您可以合并两个选择并在两者上执行操作,而无需重复代码。 "
这是一个在力导向图表中被认为是直接使用它的示例,其中每个刻度都会调用刻度函数:
var simulation = d3.forceSimulation(nodes)
.force("charge", chargeForce)
.force("center", centerForce)
.on("tick", ticked);
function ticked()
var u = d3.select("svg").selectAll("circle").data(nodes)
u.enter().append("circle").attr("r",5)
.merge(u) // What is the merge function doing here?
.attr("cx", d => d.x)
.attr("cy", d => d.y)
u.exit().remove() // Why is it necessary to remove excess objects w/ the exit selection?
我了解数据绑定的工作原理,以及 enter() 和 exit() 选择的工作原理。但是,我以前从未使用过“合并”,我不明白它在这里做什么。如果有人可以逐步简要介绍此功能中发生的事情,那将非常有用。我相信其他人也有类似的问题。
【问题讨论】:
你从哪里得到这个例子的?您应该不在 tick 函数中添加元素或重新绑定数据。 很难理解的是,您有 2 个选择并将它们组合成 1 个选择。就像你有 2 个数组,A 和 B。你可以将它们连接成数组 C。 @EricGuan 你说的不正确:merge()
不连接选择。 API 明确声明:“此方法不适用于连接任意选择,但是:如果此选择和指定的其他选择在同一索引处都有(非空)元素,此选择的元素在合并中返回,而另一个选择的元素被忽略。".
@GerardoFurtado 我在这里找到了:d3indepth.com/force-layout,为什么这样做没有意义?
@HarryCramer 这很容易理解:为什么要重新绑定数据并每秒计算数十次进入和退出选择,如果数据没有改变乙>?这没有道理!我刚刚用替代代码写了一个解释它的答案。
【参考方案1】:
文档很好地解释了该函数的作用,因此它的作用是您不必这样做
u.attr("cx", d => d.x)
.attr("cy", d => d.y);
u.enter().append("circle").attr("r",5)
.attr("cx", d => d.x)
.attr("cy", d => d.y);
您只需拨打attr
一次即可
u.enter().append("circle").attr("r",5)
.merge(u) // after this point, any updates will apply to both u and u.enter() selections
.attr("cx", d => d.x)
.attr("cy", d => d.y)
它将在u
-更新选择和u.enter()
-输入选择上设置属性cx
和cy
为什么有必要通过退出选择删除多余的对象?
因为退出选择包含任何额外的 DOM 元素,这些元素未绑定到您传递给 data()
的数组中的元素,所以您可以对退出集合执行任何您需要的操作,例如通过调用 u.exit().style(...)
设置样式等,而不是调用 remove
从 DOM 中删除它们
【讨论】:
【参考方案2】:这里实际上有两个问题:
-
了解
merge()
方法;
了解您共享的那段代码;
关于#1,您已经收到了答复。关于#2,这是我的两分钱:该代码确实不有意义。
这很容易理解:ticked
函数每秒运行几十次。 如果数据没有改变,你为什么要重新绑定数据并重新分配更新、进入和退出选择每秒数十次? (值得一提的是,那段代码的作者是个不错的程序员,这里发生了一些奇怪的事情……毕竟我们都会犯错误)
ticked
函数只需要计算元素的位置即可。
这是您与ticked
函数链接的相同代码,简化为:
function ticked()
u.attr('cx', function(d)
return d.x;
)
.attr('cy', function(d)
return d.y;
)
这里是运行代码:
var width = 600,
height = 400;
var colorScale = ['orange', 'lightblue', '#B19CD9'];
var xCenter = [100, 300, 500]
var numNodes = 100;
var nodes = d3.range(numNodes).map(function(d, i)
return
radius: Math.random() * 25,
category: i % 3
);
var u = d3.select('svg g')
.selectAll('circle')
.data(nodes);
var enter = u.enter()
.append('circle')
.attr('r', function(d)
return d.radius;
)
.style('fill', function(d)
return colorScale[d.category];
);
u = enter.merge(u);
u.exit().remove();
var simulation = d3.forceSimulation(nodes)
.force('charge', d3.forceManyBody().strength(5))
.force('x', d3.forceX().x(function(d)
return xCenter[d.category];
))
.force('collision', d3.forceCollide().radius(function(d)
return d.radius;
))
.on('tick', ticked);
function ticked()
u.attr('cx', function(d)
return d.x;
)
.attr('cy', function(d)
return d.y;
)
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id="content">
<svg >
<g transform="translate(50, 200)"></g>
</svg>
</div>
【讨论】:
谢谢 Gerardo,这很有帮助。我很困惑为什么需要重新选择,所以你的澄清很好。当你遇到这样的事情时,你永远不会知道自学。 不用担心。您可以随时向该代码的作者发送电子邮件,询问他为什么这样做。正如我所说,这对我来说没有多大意义。 "勾选的功能只需要设置位置" - 谢谢!【参考方案3】:鉴于 cmets,我应该提醒读者这个答案可能是错误的。所以请先阅读cmets
TL;DR - 合并将两个节点集合合二为一
var x = d3.selectAll(".node");
var y = d3.selectAll(".link");
var z = x.merge(y);
z 现在包含 x 中的所有元素和 y 中的所有元素。
【讨论】:
那不正确。 x 和 y 将共享索引,因此您最终只会得到两个选择之一。准确地说 z 将只是 x 选择 @40detectives 我在发布之前测试了这段代码,效果很好 我看到@40detectives 描述的行为。 对不起,这个答案肯定是错误的,merge()
是为了结合selectAll()
和enter()
的结果,从第一个开始填写null
条目。要获得组合选择,您可以:selectAll('.node,.link')
.以上是关于D3 .merge 函数的主要内容,如果未能解决你的问题,请参考以下文章
R语言merge函数全连接dataframe数据(Full (outer) join)merge函数进行全连接必须将参数all设置为true(all=TRUE)默认merge函数通过公共列名合并数
R语言merge函数连接多个dataframe数据集迭代内连接dataframe数据( iteratively merge data frames in R)默认merge函数通过公共列名合并数据