无法改变使用 networkD3 的 forceNetwork 创建的网络图中的 linkDistance

Unable to vary linkDistance in network graph created using forceNetwork of networkD3

我正在尝试使用 networkD3 包在 R 中创建力导向网络图。一切正常...

devtools::install_github('christophergandrud/networkD3')
library(networkD3)
links2 <- data.frame(
  Source = c(0, 0, 0, 1, 2, 3), 
  Target = c(1, 4, 5, 3, 4, 5), 
  Value = c(1, 9.9, 10, 8.8, 6.6, 7.2))
nodes2 <- data.frame(ID = 0:5, 
  Group = c(1L, 1L, 1L, 2L, 1L, 2L))

# this works
forceNetwork(Links=links2, Nodes=nodes2, 
  Source="Source", Target="Target", Value="Value", 
  NodeID="ID", Group="Group")

... 直到我开始使用 linkDistance 参数。当我将其设置为我想要的 Value 的功能时,我得到一个网络图,其中包含设备左上角的单个节点。

# this doesn't work
forceNetwork(Links=links2, Nodes=nodes2, 
  Source="Source", Target="Target", Value="Value", 
  NodeID="ID", Group="Group",
  linkDistance="function(d) { return d.value; }")

如有任何关于如何改变链接长度的建议,我将不胜感激。

我在 R Studio 版本 0.98.1103 中为 Windows 使用 R 版本 3.1.3,包 networkD3 版本 0.1.2.2。 (我最初使用来自 CRAN 的 networkD3 版本 0.1.2.1 遇到了这个问题。所以我从 GitHub 安装了最新版本并遇到了同样的问题。)

networkD3 包太棒了!!

经过详尽的调查,我找到了错误:networkD3 seems to depend on d3js 生成的 HTML/CSS/JavaScript 代码,它提供了生成的 d3.min.js 文件的核心网络代码。 linkDistance 特性是在 d3.min.js 中编码的,错误就是在那里发生的。

不幸的是,正如文件名所示,JavaScript 已缩小,但您可以从 d3js 站点访问未缩小的源代码,特别是 https://github.com/mbostock/d3/blob/master/d3.js

如果您查看 d3.js 的第 6307 行(和周围的上下文),您会发现:

force.linkDistance = function(x) {
  if (!arguments.length) return linkDistance;
  linkDistance = typeof x === "function" ? x : +x;
  return force;
};

此函数在初始化期间的某个时刻被调用,x 是您最初在对 forceNetwork() 的 R 调用中提供的 字符串 (顺便说一句,该字符串作为 JSON 数据以及所有其他输入数据嵌入到 index.html 文件中,该文件由 networkD3 生成。

重复一下,x 是一个 字符串 。这意味着 typeof(x) 将 return "string" 而不是 "function",因为第 6307 行正在检查。因此,将调用三元的第二个替代方案,+x,这是 JavaScript 式强制转换为 number,这将导致 NaN。因此,NaN 通过所有 link 距离和坐标传播,一切都得到 f*!%ed。

这可以通过将其更改为来解决:

linkDistance = typeof x === "string" ? eval('('+x+')') : +x;

显然我将 "function" 更改为 "string",但我还必须添加一个 eval() 调用以实际将函数定义从字符串中拉出,以便所有未来的代码都能正常工作with linkDistance 会起作用,因为它都假定它是一个函数,而不是一个字符串。最后,我不得不用括号连接以确保函数定义被表达,否则,如果您使用 function 作为代码字符串的前导标记(您这样做了,他们在 networkD3文档,这是最明智的做法),那么它将被视为函数语句,这意味着它不会被解析为实际的表达式语句,并且 eval() 调用将 return undefined,当代码最终尝试在算术表达式中使用 undefined 值时,您最终会得到 NaN,从而导致相同的错误。 (请参阅 What is the difference between a function expression vs declaration in JavaScript? and https://javascriptweblog.wordpress.com/2010/07/06/function-declarations-vs-function-expressions/ 了解有关此区别的几个很好的来源,并且可能还有一百万个其他来源。)

我已经在我的机器上测试了上面的修复并且它有效。我想这应该作为一个错误报告给 d3js 人,但我觉得我已经在这里做了足够的工作并且不会被打扰。请随时向他们报告此事,使用我的 post 此处作为参考。

如果您希望对从 R 传递到 JavaScript 的值进行求值(即将字符串解析为函数),请将其包含在 htmlwidgets::JS 函数中。例如:

library(htmlwidgets)
forceNetwork(Links=links2, Nodes=nodes2, 
             Source="Source", Target="Target", Value="Value", 
             NodeID="ID", Group="Group",
             linkDistance=JS('function(d) {', 'return d.value;', '}'))