I'm working on a project where I'm visualizing a network graph using the networkD3 package in R. The network consists of nodes and edges, represented by two data frames: node_df and edge_df, respectively. The node_df data frame contains information about the nodes, including their IDs, labels, groups, and colors. The edge_df data frame contains information about the edges, including the source and target nodes, as well as edge labels.
I've tried various approaches to visualize the network graph and add interactivity to it, adding tooltips to nodes and edges. However, I haven't been successful on adding the tooltips on the arrows when hovering the mouse.
Here's a summary of the code I've tried so far:
Data
> head(edge_df, 10)
id from to rel label penwidth color fontname fontsize weight constraint
1 1 2 16 <NA> 183 5.000000 dodgerblue4 Arial 10 1 TRUE
2 2 3 6 <NA> 1 1.021858 dodgerblue4 Arial 10 1 TRUE
3 3 3 11 <NA> 10 1.218579 dodgerblue4 Arial 10 1 TRUE
4 4 3 17 <NA> 5 1.109290 dodgerblue4 Arial 10 1 TRUE
5 5 3 19 <NA> 2 1.043716 dodgerblue4 Arial 10 1 TRUE
6 6 4 4 <NA> 2 1.043716 dodgerblue4 Arial 10 1 TRUE
7 7 4 21 <NA> 1 1.021858 dodgerblue4 Arial 10 1 TRUE
8 8 5 17 <NA> 17 1.371585 dodgerblue4 Arial 10 1 TRUE
9 9 6 6 <NA> 1 1.021858 dodgerblue4 Arial 10 1 TRUE
10 10 6 10 <NA> 5 1.109290 dodgerblue4 Arial 10 1 TRUE
> head(node_df, 10)
id type label shape color_level style fontcolor color tooltip
1 1 <NA> End circle Inf rounded,filled brown4 brown4 ARTIFICIAL_END\n183
2 2 <NA> Start circle Inf rounded,filled chartreuse4 chartreuse4 ARTIFICIAL_START\n183
3 3 <NA> Analyze Done\n18 rectangle 0.08333333 rounded,filled black grey Analyze Done\n18
4 4 <NA> Approved\n3 rectangle 0.01388889 rounded,filled black grey Approved\n3
5 5 <NA> Back from Development\n17 rectangle 0.07870370 rounded,filled black grey Back from Development\n17
6 6 <NA> Backlog\n9 rectangle 0.04166667 rounded,filled black grey Backlog\n9
7 7 <NA> Cancelled\n1 rectangle 0.00462963 rounded,filled black grey Cancelled\n1
8 8 <NA> Closed\n138 rectangle 0.63888889 rounded,filled white grey Closed\n138
9 9 <NA> Dispatched\n166 rectangle 0.76851852 rounded,filled white grey Dispatched\n166
10 10 <NA> Done\n216 rectangle 1.00000000 rounded,filled white grey Done\n216
penwidth fixedsize fontname fontsize fillcolor Group
1 1.5 FALSE Arial 10 white End
2 1.5 FALSE Arial 10 white Start
3 1.5 FALSE Arial 10 #ECE7F2 Analyze Done\n18
4 1.5 FALSE Arial 10 #FFF7FB Approved\n3
5 1.5 FALSE Arial 10 #ECE7F2 Back from Development\n17
6 1.5 FALSE Arial 10 #FFF7FB Backlog\n9
7 1.5 FALSE Arial 10 #FFF7FB Cancelled\n1
8 1.5 FALSE Arial 10 #74A9CF Closed\n138
9 1.5 FALSE Arial 10 #3690C0 Dispatched\n166
10 1.5 FALSE Arial 10 #034E7B Done\n216
Code
# Add a dummy "Group" column to nodes dataframe
node_df$Group <- node_df$label
#
edges_net <- edge_df[, c("from", "to", "label")]
colnames(edges_net) <- c("from", "to", "title")
nodes_net <- node_df[, c("id", "label", "Group", "fillcolor")]
colnames(nodes_net) <- c("id", "node_label", "Group", "nodes_color")
# Subtract 1 from the "from" and "to" columns to zero-index them
edges_net$from <- edges_net$from - 1
edges_net$to <- edges_net$to - 1
# set node size
nodes_net$NodeSize <- 20
# JS
clickJS <- "
d3.selectAll('.xtooltip').remove();
d3.select('body').append('div')
.attr('class', 'xtooltip')
.style('position', 'absolute')
.style('border', '1px solid #999')
.style('border-radius', '3px')
.style('padding', '5px')
.style('opacity', '0.85')
.style('background-color', '#fff')
.style('box-shadow', '2px 2px 6px #888888')
.html('name: ' + d.name + '<br>' + 'group: ' + d.group)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
"
# plot network
my_network<- networkD3::forceNetwork(
Links = edges_net,
Nodes = nodes_net,
Source = "from",
Target = "to",
Value = "title",
NodeID = "node_label",
Group = "Group",
colourScale = networkD3::JS("d3.scaleOrdinal(d3.schemeCategory20);"),
linkDistance = 300,
linkWidth = networkD3::JS("function(d) { return Math.sqrt(d.value);}"),
radiusCalculation = networkD3::JS(" Math.sqrt(d.nodesize)+6"),
Nodesize = "NodeSize",
charge = - 30,,
linkColour = "black",
opacity = 0.8,
zoom = T,
legend = T,
arrows = T,
bounded = T,
opacityNoHover = 1.5,
fontSize = 12,
clickAction = clickJS
#
)
# Increase the size of nodes
my_network$x$width <- '1200px'
my_network$x$height <- '800px'
# Get the target variable in fn$x$links (an integer id) to show up as a tooltip when user hovers over a link (i.e. edge) in the graph
fnrender <- htmlwidgets::onRender(
my_network,
'
function(el, x) {
d3.selectAll(".link").append("svg:title")
.text(function(d) { return d.source.name + " -> " + d.target.name; })
}
'
)
# display the result
fnrender
Produced graph
The objective of my project is to create an interactive network visualization where users can hover on nodes to view additional information and hover over edges to see edge labels.
I modified your code so that it's reproducible and tried it. I see the tooltip on the links, though on my browser you have to hover for about 0.5-1 second before it shows up.
edge_df <- tibble::tribble(
~id, ~from, ~to, ~rel, ~label, ~penwidth, ~color, ~fontname, ~fontsize, ~weight, ~constraint,
1, 1, 6, NA, 183, 5.000000, "dodgerblue4", "Arial", 10, 1, TRUE,
2, 2, 5, NA, 1, 1.021858, "dodgerblue4", "Arial", 10, 1, TRUE,
3, 2, 7, NA, 10, 1.218579, "dodgerblue4", "Arial", 10, 1, TRUE,
4, 2, 8, NA, 5, 1.109290, "dodgerblue4", "Arial", 10, 1, TRUE,
5, 2, 9, NA, 2, 1.043716, "dodgerblue4", "Arial", 10, 1, TRUE,
6, 3, 3, NA, 2, 1.043716, "dodgerblue4", "Arial", 10, 1, TRUE,
7, 3, 10, NA, 1, 1.021858, "dodgerblue4", "Arial", 10, 1, TRUE,
8, 4, 6, NA, 17, 1.371585, "dodgerblue4", "Arial", 10, 1, TRUE,
9, 5, 5, NA, 1, 1.021858, "dodgerblue4", "Arial", 10, 1, TRUE,
10, 5, 10, NA, 5, 1.109290, "dodgerblue4", "Arial", 10, 1, TRUE
)
node_df <- tibble::tribble(
~id, ~type, ~label, ~shape, ~color_level, ~style, ~fontcolor, ~color, ~tooltip, ~penwidth, ~fixedsize, ~fontname, ~fontsize, ~fillcolor, ~Group,
1, NA, "End", "circle", Inf, "rounded,filled", "brown4", "brown4", "ARTIFICIAL_END\n1831", 1.5, FALSE, "Arial", 10, "white", "End",
2, NA, "Start", "circle", Inf, "rounded,filled", "chartreuse4", "chartreuse4", "ARTIFICIAL_START\n1832", 1.5, FALSE, "Arial", 10, "white", "Start",
3, NA, "Analyze Done\n18", "rectangle", 0.08333333, "rounded,filled", "black", "grey", "Analyze Done\n183", 1.5, FALSE, "Arial", 10, "#ECE7F2", "Analyze Done\n18",
4, NA, "Approved\n3", "rectangle", 0.01388889, "rounded,filled", "black", "grey", "Approved\n34", 1.5, FALSE, "Arial", 10, "#FFF7FB", "Approved\n3",
5, NA,"Back from Development\n17", "rectangle", 0.07870370, "rounded,filled", "black", "grey", "Back from Development\n175", 1.5, FALSE, "Arial", 10, "#ECE7F2", "Back from Development\n17",
6, NA, "Backlog\n9", "rectangle", 0.04166667, "rounded,filled", "black", "grey", "Backlog\n96", 1.5, FALSE, "Arial", 10, "#FFF7FB", "Backlog\n9",
7, NA, "Cancelled\n1", "rectangle", 0.00462963, "rounded,filled", "black", "grey", "Cancelled\n17", 1.5, FALSE, "Arial", 10, "#FFF7FB", "Cancelled\n1",
8, NA, "Closed\n138", "rectangle", 0.63888889, "rounded,filled", "white", "grey", "Closed\n1388", 1.5, FALSE, "Arial", 10, "#74A9CF", "Closed\n138",
9, NA, "Dispatched\n166", "rectangle", 0.76851852, "rounded,filled", "white", "grey", "Dispatched\n1669", 1.5, FALSE, "Arial", 10, "#3690C0", "Dispatched\n166",
10, NA, "Done\n216", "rectangle", 1.00000000, "rounded,filled", "white", "grey", "Done\n21610", 1.5, FALSE, "Arial", 10, "#034E7B", "Done\n216"
)
# Add a dummy "Group" column to nodes dataframe
node_df$Group <- node_df$label
#
edges_net <- edge_df[, c("from", "to", "label")]
colnames(edges_net) <- c("from", "to", "title")
nodes_net <- node_df[, c("id", "label", "Group", "fillcolor")]
colnames(nodes_net) <- c("id", "node_label", "Group", "nodes_color")
# Subtract 1 from the "from" and "to" columns to zero-index them
edges_net$from <- edges_net$from - 1
edges_net$to <- edges_net$to - 1
# set node size
nodes_net$NodeSize <- 20
# JS
clickJS <- "
d3.selectAll('.xtooltip').remove();
d3.select('body').append('div')
.attr('class', 'xtooltip')
.style('position', 'absolute')
.style('border', '1px solid #999')
.style('border-radius', '3px')
.style('padding', '5px')
.style('opacity', '0.85')
.style('background-color', '#fff')
.style('box-shadow', '2px 2px 6px #888888')
.html('name: ' + d.name + '<br>' + 'group: ' + d.group)
.style('left', (d3.event.pageX) + 'px')
.style('top', (d3.event.pageY - 28) + 'px');
"
# plot network
my_network<- networkD3::forceNetwork(
Links = edges_net,
Nodes = nodes_net,
Source = "from",
Target = "to",
Value = "title",
NodeID = "node_label",
Group = "Group",
colourScale = networkD3::JS("d3.scaleOrdinal(d3.schemeCategory20);"),
linkDistance = 300,
linkWidth = networkD3::JS("function(d) { return Math.sqrt(d.value);}"),
radiusCalculation = networkD3::JS(" Math.sqrt(d.nodesize)+6"),
Nodesize = "NodeSize",
charge = - 30,,
linkColour = "black",
opacity = 0.8,
zoom = T,
legend = T,
arrows = T,
bounded = T,
opacityNoHover = 1.5,
fontSize = 12,
clickAction = clickJS
#
)
#> Links is a tbl_df. Converting to a plain data frame.
#> Nodes is a tbl_df. Converting to a plain data frame.
# Increase the size of nodes
my_network$x$width <- '1200px'
my_network$x$height <- '800px'
# Get the target variable in fn$x$links (an integer id) to show up as a tooltip when user hovers over a link (i.e. edge) in the graph
fnrender <- htmlwidgets::onRender(
my_network,
'
function(el, x) {
d3.selectAll(".link").append("svg:title")
.text(function(d) { return d.source.name + " -> " + d.target.name; })
}
'
)
# display the result
fnrender