Skip to content

Instantly share code, notes, and snippets.

@christophergandrud
Last active March 7, 2017 07:28
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save christophergandrud/5734624 to your computer and use it in GitHub Desktop.
Save christophergandrud/5734624 to your computer and use it in GitHub Desktop.
An R function for creating simple D3 javascript directed network graphs.
#' An R function for creating simple D3 javascript directed network graphs.
#'
#' d3SimpleNetwork creates simple D3 javascript network graphs.
#'
#' @param data a data frame object with three columns. The first two are the names of the linked units. The third records an edge value. (Currently the third column doesn't affect the graph.)
#' @param Source character string naming the network source variable in the data frame. If \code{Source = NULL} then the first column of the data frame is treated as the source.
#' @param Target character string naming the network target variable in the data frame. If \code{Target = NULL} then the second column of the data frame is treated as the target.
#' @param height numeric height for the network graph's frame area.
#' @param width numeric width for the network graph's frame area.
#' @param file a character string of the file name to save the resulting graph. If a file name is given a standalone webpage is created, i.e. with a header and footer. If \code{file = NULL} then just the graph is returned to the console. This is useful if you are using knitr Markdown. If you set the knitr code chunk \code{results='asis'} then the graph will be rendered in the output.
#'
#' @source
#' d3.js was created by Michael Bostock. See http://d3js.org/
#' Draws on code from: http://theweiluo.wordpress.com/2011/09/30/r-to-json-for-d3-js-and-protovis/
#'
d3SimpleNetwork <- function(Data, Source = NULL, Target = NULL, height = 600, width = 900, file = NULL)
{
if (class(Data) != "data.frame"){
stop("data must be a data frame class object.")
}
if (is.null(Source) & is.null(Target)){
NetData <- Data[, 1:2]
}
else if (!is.null(Source) & !is.null(Target)){
NetData <- data.frame(Data[, Source], Data[, Target])
}
names(NetData) <- c("source", "target")
# Function from: http://theweiluo.wordpress.com/2011/09/30/r-to-json-for-d3-js-and-protovis/
toJSONarray <- function(dtf){
clnms <- colnames(dtf)
name.value <- function(i){
quote <- '';
if(class(dtf[, i])!='numeric'){
quote <- '"';
}
paste('"', i, '" : ', quote, dtf[,i], quote, sep='')
}
objs <- apply(sapply(clnms, name.value), 1, function(x){paste(x, collapse=', ')})
objs <- paste('{', objs, '}')
res <- paste('[', paste(objs, collapse=', '), ']')
return(res)
}
# Convert data frame to JSON format
LinkData <- toJSONarray(NetData)
LinkData <- paste("var links =", LinkData, "; \n")
# Create webpage
PageHead <- "
<!DOCTYPE html>
<meta charset=\"utf-8\">
<body> \n"
NetworkCSS <- "
<script src=\"http://d3js.org/d3.v2.js?2.9.1\"></script>
<style>
.link {
stroke: #666;
opacity: 0.6;
stroke-width: 1.5px;
}
.node circle {
stroke: #fff;
opacity: 0.6;
stroke-width: 1.5px;
}
text {
font: 10px serif;
pointer-events: none;
}
</style>
<script> \n "
# width and height variables
HeightWidth <- paste("var width =", width, "\n",
"height =", height, "; \n")
# Main script for creating the graph
MainScript <- "
var nodes = {}
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] ||
(nodes[link.source] = {name: link.source});
link.target = nodes[link.target] ||
(nodes[link.target] = {name: link.target});
link.value = +link.value;
});
var color = d3.scale.category20();
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.size([width, height])
.linkDistance(80)
.charge(-400)
.on(\"tick\", tick)
.start();
var svg = d3.select(\"body\").append(\"svg\")
.attr(\"width\", width)
.attr(\"height\", height);
var link = svg.selectAll(\".link\")
.data(force.links())
.enter().append(\"line\")
.attr(\"class\", \"link\");
var node = svg.selectAll(\".node\")
.data(force.nodes())
.enter().append(\"g\")
.attr(\"class\", \"node\")
.on(\"mouseover\", mouseover)
.on(\"mouseout\", mouseout)
.on(\"click\", click)
.on(\"dblclick\", dblclick)
.call(force.drag);
node.append(\"circle\")
.attr(\"r\", 8)
.style(\"fill\", function(d) { return color(d.value); });
node.append(\"text\")
.attr(\"x\", 12)
.attr(\"dy\", \".35em\")
.style(\"fill\", \"steelblue\")
.text(function(d) { return d.name; });
function tick() {
link
.attr(\"x1\", function(d) { return d.source.x; })
.attr(\"y1\", function(d) { return d.source.y; })
.attr(\"x2\", function(d) { return d.target.x; })
.attr(\"y2\", function(d) { return d.target.y; });
node
.attr(\"transform\", function(d) { return \"translate(\" + d.x + \",\" + d.y + \")\"; });
}
function mouseover() {
d3.select(this).select(\"circle\").transition()
.duration(750)
.attr(\"r\", 16);
}
function mouseout() {
d3.select(this).select(\"circle\").transition()
.duration(750)
.attr(\"r\", 8);
}
// action to take on mouse click
function click() {
d3.select(this).select(\"text\").transition()
.duration(750)
.attr(\"x\", 22)
.style(\"stroke-width\", \".5px\")
.style(\"fill\", \"#E34A33\")
.style(\"font\", \"20px serif\");
d3.select(this).select(\"circle\").transition()
.duration(750)
.style(\"fill\", \"#E34A33\")
.attr(\"r\", 16)
}
// action to take on mouse double click
function dblclick() {
d3.select(this).select(\"circle\").transition()
.duration(750)
.attr(\"r\", 6)
.style(\"fill\", \"#E34A33\");
d3.select(this).select(\"text\").transition()
.duration(750)
.attr(\"x\", 12)
.style(\"stroke\", \"none\")
.style(\"fill\", \"#E34A33\")
.style(\"stroke\", \"none\")
.style(\"font\", \"10px serif\");
}
</script> \n"
if (is.null(file)){
cat(NetworkCSS, LinkData, HeightWidth, MainScript)
}
else if (!is.null(file)){
cat(PageHead, NetworkCSS, LinkData, HeightWidth, MainScript,
"</body>", file = file)
}
}
@FinScience
Copy link

Hello...I am using this function for my dataframe. However, I am unable to understand where is the graph or the JSON hierarchical structure is being stored. Any guidance would be great.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment