Search code examples
rgraphnodesvisnetwork

Adding additional information to a "visNetwork"


Using R, I create some fake data about a group of people and their relationships to each other:

#relationship data

Data_I_Have <- data.frame(
   
    "Node_A" = c("John", "John", "John", "Peter", "Peter", "Peter", "Tim", "Kevin", "Adam", "Adam", "Xavier"),
    "Node_B" = c("Claude", "Peter", "Tim", "Tim", "Claude", "Henry", "Kevin", "Claude", "Tim", "Henry", "Claude"),
    " Place_Where_They_Met" = c("Chicago", "Boston", "Seattle", "Boston", "Paris", "Paris", "Chicago", "London", "Chicago", "London", "Paris"),
  "Years_They_Have_Known_Each_Other" = c("10", "10", "1", "5", "2", "8", "7", "10", "3", "3", "5"),
  "What_They_Have_In_Common" = c("Sports", "Movies", "Computers", "Computers", "Video Games", "Sports", "Movies", "Computers", "Sports", "Sports", "Video Games")
)

#data about individuals

additional_data_about_people <- data.frame(
   
    "Person" = c("John", "Peter", "Tim", "Kevin", "Adam", "Xacier", "Claude", "Henry"),
   "Job" = c("Teacher", "Lawyer", "Accountant", "Engineer", "Teacher", "Lawyer", "Engineer", "Lawyer"),
"Age" = c("50", "51", "61", "56", "65", "65", "54", "50"),
"Favorite_Food" = c("pizza", "pizza", "tacos", "pizza", "ice cream", "sushi", "sushi", "pizza")
)

Using this information, I was able to successfully make a graph network representing the relationships between these people:

library(igraph)
library(dplyr)
library(visNetwork)


graph_file <- data.frame(Data_I_Have$Node_A, Data_I_Have$Node_B)


colnames(graph_file) <- c("Data_I_Have$Node_A", "Data_I_Have$Node_B")

graph <- graph.data.frame(graph_file, directed=F)
graph <- simplify(graph)

plot(graph)

nodes <- data.frame(id = V(graph)$name, title = V(graph)$name)
nodes <- nodes[order(nodes$id, decreasing = F),]
edges <- get.data.frame(graph, what="edges")[1:2]

visNetwork(nodes, edges) %>%   visIgraphLayout(layout = "layout_with_fr") %>%
    visOptions(highlightNearest = TRUE, nodesIdSelection = TRUE)

I figured, it would be useful if I could display the information about each person when the user clicks on the node, as well as the details of their relationship (if possible).

I have tried using the "visEvents" and "title" option (https://datastorm-open.github.io/visNetwork/nodes.html) in R, but I can't seem to figure it out. Could someone please show me how to do this?

Thanks


Solution

  • Data_I_Have <- data.frame(
    
      "Node_A" = c("John", "John", "John", "Peter", "Peter", "Peter", "Tim", "Kevin", "Adam", "Adam", "Xavier"),
      "Node_B" = c("Claude", "Peter", "Tim", "Tim", "Claude", "Henry", "Kevin", "Claude", "Tim", "Henry", "Claude"),
      "Place_Where_They_Met" = c("Chicago", "Boston", "Seattle", "Boston", "Paris", "Paris", "Chicago", "London", "Chicago", "London", "Paris"),
      "Years_They_Have_Known_Each_Other" = c("10", "10", "1", "5", "2", "8", "7", "10", "3", "3", "5"),
      "What_They_Have_In_Common" = c("Sports", "Movies", "Computers", "Computers", "Video Games", "Sports", "Movies", "Computers", "Sports", "Sports", "Video Games")
    )
    
    common_data = purrr::imap_dfc(dplyr::select(Data_I_Have, -Node_A, -Node_B), function(item, id){
      paste(id, "&colon; ", item)
    })
    
    common_strings = purrr::map_chr(seq(1, nrow(common_data)), function(in_row){
      paste(common_data[in_row, ], collapse = "<br>")
    })
    
    edge_data = dplyr::transmute(Data_I_Have, from = Node_A, to = Node_B, title = common_strings)
    
    #data about individualsli
    
    additional_data_about_people <- data.frame(
    
      "Person" = c("John", "Peter", "Tim", "Kevin", "Adam", "Xacier", "Claude", "Henry"),
      "Job" = c("Teacher", "Lawyer", "Accountant", "Engineer", "Teacher", "Lawyer", "Engineer", "Lawyer"),
      "Age" = c("50", "51", "61", "56", "65", "65", "54", "50"),
      "Favorite_Food" = c("pizza", "pizza", "tacos", "pizza", "ice cream", "sushi", "sushi", "pizza")
    )
    
    
    library(igraph)
    library(dplyr)
    library(visNetwork)
    
    
    graph_file <- data.frame(Data_I_Have$Node_A, Data_I_Have$Node_B)
    
    
    colnames(graph_file) <- c("Data_I_Have$Node_A", "Data_I_Have$Node_B")
    
    graph <- graph.data.frame(graph_file, directed=F)
    graph <- simplify(graph)
    
    plot(graph)
    
    add_field = purrr::imap_dfc(additional_data_about_people, function(item, id){
      paste0(id, "&colon; ", item)
    })
    additional_strings = purrr::map_chr(seq(1, nrow(add_field)), function(in_row){
      paste(add_field[in_row, ], collapse = "<br>")
    })
    additional_df = data.frame(id = additional_data_about_people$Person, title = additional_strings)
    additional_df2 = dplyr::left_join(data.frame(id = V(graph)$name), additional_df, by = "id")
    
    
    nodes <- data.frame(id = V(graph)$name, title = additional_df2$title)
    nodes <- nodes[order(nodes$id, decreasing = F),]
    edges <- get.data.frame(graph, what="edges")[1:2]
    
    edges2 = dplyr::left_join(edges, edge_data, by = c("from", "to"))
    
    
    visNetwork(nodes, edges2)
    

    Then on hover, I see the additional information about each node and edge.

    Two things to be mindful of here:

    1. visNetwork displays in html, so you have to use html codes for special characters, like the <br> for a return, and &colon; for the ":".
    2. Everything can have a "title" attribute that gets displayed as a tooltip, so you can add it to the edges as well.

    Notice I create the data.frame where I have the attribute added to make it look field like with the ":", and then paste them all together to make the actual title to display.

    Hopefully the above makes sense.

    Also, fix your code, you had a space in front of one variable name that was going to do weird things to it.

    As far as displaying the names when you click on them, that's beyond me at the moment.