I'm trying to make an interactive network/mind map application in shiny with visNetwork. visNetwork allows for interactive creation and manipulation of a network graph, how can I save these results in the R data.frame?
I have looked at the documentation, but have not been able to understand how to extract the changes.
Below is the code of the shiny app.
ui <- navbarPage(
# Application title
"Old Faithful Geyser Data",
tabPanel("Plot"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
mainPanel(
visNetworkOutput("network")
)
)
)
server <- function(input, output) {
output$network <- renderVisNetwork({
nodes <- data.frame(id = 1,
label = 1)
edges <- data.frame(from = as.numeric(),
to = as.numeric())
visNetwork(nodes, edges) %>%
visEdges(arrows = "to") %>%
visHierarchicalLayout(direction = "RL", levelSeparation = 500) %>%
visOptions(manipulation = TRUE)
observe({
visNetworkProxy("network_proxy_nodes") %>%
visUpdateNodes(nodes = input$mynetwork__graphChange)
})
})
}
The desired results are to add changes into the nodes and edges data frames; example.
nodes <- data.frame(id = c(1:2),
label = c(1:2))
edges <- data.frame(from = 1,
to = 2)
How do I achieve this?
The input$[my network name]_graphChange
element created by the manipulation
option returns a list describing what the user just changed. We can use that information to update our main data frames of nodes and edges.
This code displays a graph that the user can edit, and two tables showing all the nodes and edges currently in the graph, including the user's changes. (This answer provided helpful guidance on how to use reactiveValues
to store a dynamically updated data frame.)
require(shiny)
require(visNetwork)
library(dplyr)
# Initialize the graph with these nodes/edges. We have to assign edges an ID
# in case the user edits them later.
init.nodes.df = data.frame(id = c("foo", "bar"),
label = c("Foo", "Bar"),
stringsAsFactors = F)
init.edges.df = data.frame(id = "foobar",
from = "foo",
to = "bar",
stringsAsFactors = F)
ui <- fluidPage(
fluidRow(
# Display two tables: one with the nodes, one with the edges.
column(
width = 6,
tags$h1("Nodes in the graph:"),
tableOutput("all_nodes"),
tags$h1("Edges in the graph:"),
tableOutput("all_edges")
),
# The graph.
column(
width = 6,
visNetworkOutput("editable_network", height = "400px")
)
)
)
server <- function(input, output) {
# `graph_data` is a list of two data frames: one of nodes, one of edges.
graph_data = reactiveValues(
nodes = init.nodes.df,
edges = init.edges.df
)
# Render the graph.
output$editable_network <- renderVisNetwork({
visNetwork(graph_data$nodes, graph_data$edges) %>%
visOptions(manipulation = T)
})
# If the user edits the graph, this shows up in
# `input$[name_of_the_graph_output]_graphChange`. This is a list whose
# members depend on whether the user added a node or an edge. The "cmd"
# element tells us what the user did.
observeEvent(input$editable_network_graphChange, {
# If the user added a node, add it to the data frame of nodes.
if(input$editable_network_graphChange$cmd == "addNode") {
temp = bind_rows(
graph_data$nodes,
data.frame(id = input$editable_network_graphChange$id,
label = input$editable_network_graphChange$label,
stringsAsFactors = F)
)
graph_data$nodes = temp
}
# If the user added an edge, add it to the data frame of edges.
else if(input$editable_network_graphChange$cmd == "addEdge") {
temp = bind_rows(
graph_data$edges,
data.frame(id = input$editable_network_graphChange$id,
from = input$editable_network_graphChange$from,
to = input$editable_network_graphChange$to,
stringsAsFactors = F)
)
graph_data$edges = temp
}
# If the user edited a node, update that record.
else if(input$editable_network_graphChange$cmd == "editNode") {
temp = graph_data$nodes
temp$label[temp$id == input$editable_network_graphChange$id] = input$editable_network_graphChange$label
graph_data$nodes = temp
}
# If the user edited an edge, update that record.
else if(input$editable_network_graphChange$cmd == "editEdge") {
temp = graph_data$edges
temp$from[temp$id == input$editable_network_graphChange$id] = input$editable_network_graphChange$from
temp$to[temp$id == input$editable_network_graphChange$id] = input$editable_network_graphChange$to
graph_data$edges = temp
}
# If the user deleted something, remove those records.
else if(input$editable_network_graphChange$cmd == "deleteElements") {
for(node.id in input$editable_network_graphChange$nodes) {
temp = graph_data$nodes
temp = temp[temp$id != node.id,]
graph_data$nodes = temp
}
for(edge.id in input$editable_network_graphChange$edges) {
temp = graph_data$edges
temp = temp[temp$id != edge.id,]
graph_data$edges = temp
}
}
})
# Render the table showing all the nodes in the graph.
output$all_nodes = renderTable({
graph_data$nodes
})
# Render the table showing all the edges in the graph.
output$all_edges = renderTable({
graph_data$edges
})
}
shinyApp(ui = ui, server = server)