Я работаю над проектом, в котором визуализирую сетевой граф, используя пакет networkD3 в R. Сеть состоит из узлов и ребер, представленных двумя кадрами данных: node_df и Edge_df соответственно. Кадр данных node_df содержит информацию об узлах, включая их идентификаторы, метки, группы и цвета. Кадр данных Edge_df содержит информацию о ребрах, включая исходные и целевые узлы, а также метки ребер.
Я пробовал различные подходы к визуализации сетевого графа и добавлению к нему интерактивности, добавляя подсказки к узлам и ребрам. Однако мне не удалось добавить всплывающие подсказки к стрелкам при наведении мыши.
Вот краткое изложение кода, который я пробовал до сих пор:
Данные
> 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
Код
# 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
Произведенный график
Цель моего проекта — создать интерактивную визуализацию сети, где пользователи могут наводить курсор на узлы, чтобы просмотреть дополнительную информацию, и наводить курсор на ребра, чтобы видеть метки ребер.



![Безумие обратных вызовов в javascript [JS]](https://i.imgur.com/WsjO6zJb.png)


Я изменил ваш код, чтобы его можно было воспроизвести, и попробовал. Я вижу всплывающую подсказку по ссылкам, хотя в моем браузере вам нужно навести курсор примерно на 0,5-1 секунду, прежде чем она появится.
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
