问题
I am trying to create a force directed network graph in R using the networkD3 package. Everything works well ...
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")
... until I start playing with the linkDistance
argument. When I set it to what I want, a function of the Value
, I get a network diagram that consists of what appears to be a single node in the far upper left corner of the device.
# 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; }")
I would appreciate any suggestions as to how to get the length of the links to vary.
I'm using R version 3.1.3 for Windows in R Studio Version 0.98.1103 with package networkD3 version 0.1.2.2. (I originally experienced the problem using networkD3 version 0.1.2.1 from CRAN. So I installed the latest version from GitHub, and had the same problem.)
回答1:
If you want a value passed through from R to JavaScript to be evaluated (i.e. to resolve a string to a function) you enclose it in the htmlwidgets::JS function. For example:
library(htmlwidgets)
forceNetwork(Links=links2, Nodes=nodes2,
Source="Source", Target="Target", Value="Value",
NodeID="ID", Group="Group",
linkDistance=JS('function(d) {', 'return d.value;', '}'))
回答2:
The networkD3
package is awesome!!
After an exhaustive investigation, I've tracked down the bug: The HTML/CSS/JavaScript code that is generated by networkD3 seems to depend on d3js, which provides the d3.min.js
file that is central to the generated web code. It is inside d3.min.js
that the linkDistance
feature is coded, and it is there that the bug occurs.
Unfortunately, as the file name indicates, that JavaScript is minified, but you can access the non-minified source from the d3js
site, specifically at https://github.com/mbostock/d3/blob/master/d3.js.
If you look at line 6307 (and surrounding context) of d3.js
, you'll find this:
force.linkDistance = function(x) {
if (!arguments.length) return linkDistance;
linkDistance = typeof x === "function" ? x : +x;
return force;
};
This function is called at some point during initialization with x
being the string that you originally provided in your R call to forceNetwork()
(incidentally, that string is embedded in the index.html
file that is generated by networkD3
as JSON data, along with all other input data).
To repeat, x
is a string. This means typeof(x)
will return "string"
, not "function"
, as line 6307 is checking for. Hence, the second alternative of the ternary will be invoked, +x
, which is JavaScript-style coercion to number
, which will result in NaN
. Thus, NaN
gets propagated through all the link distances and coordinates and everything gets f*!%ed.
This can be fixed by changing it to:
linkDistance = typeof x === "string" ? eval('('+x+')') : +x;
Obviously I changed "function"
to "string"
, but also I had to add an eval()
call to actually pull the function definition out of the string so that all future code that works with linkDistance
will work, because it all assumes that it's a function, not a string. Finally, I had to concatenate with parentheses to ensure that the function definition is expressionized, otherwise, if you used function
as the leading token of your code string (which you did, and they did in the networkD3
documentation, and that's the most sensible thing to do), then it would be taken as a function statement, meaning it would not be parsed as an actual expression statement, and the eval()
call would return undefined
, and you'd end up getting NaN
when the code would eventually try to use the undefined
value in arithmetic expressions, leading to the same errors. (See 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/ for a couple good sources on this distinction, and there's probably a million other sources out there as well.)
I've tested the above fix on my machine and it works. I suppose this should be reported as a bug to the d3js
people, but I feel I've done enough work here and can't be bothered. Feel free to report this to them, using my post here as a reference.
来源:https://stackoverflow.com/questions/30446519/unable-to-vary-linkdistance-in-network-graph-created-using-forcenetwork-of-netwo