library(igraph)
library(intronets)
library(threejs) # 3D Networks
library(visNetwork) # Interactive
library(htmlwidgets)
library(htmltools)
library(dplyr)Appendix B — Interactive Network Visualization
In this chapter we will learn about how to create engaging network visualizations. By engaging, we mean so literally. One way to help your audience understand a network is to have them interact with it. R offers multiple different packages all filled with fun ways to accomplish this. In this chapter, we cover 2 methods one that creates 3D networks and a second that creates interactive networks.
Let’s begin by loading the necessary packages.
B.1 The Data
We are going to use the terrorism data 1 2 that we used in our chapter on graph layouts. Just a quick refresher on this network, it was constructed based on the Jemaah Islamiyah cell that was responsible for the 2002 bombings in Bali. Ties are constructed through interactions between individuals within the terror organization. The roles are recorded for each individual as bomb maker (BM), command team (CT), operation assistant (OA), team lima (TL), and suicide bombers (SB). Below we read in the data and explore it a little.
load_nets("bali.rda")B.2 3D Networks with threejs
If you are working with other data, you will need to create an igraph object from the edgelist or adjacency matrix. Once you have an igraph network object you can then create your 3D network.
To create the 3D network we use the graphjs() function which simply converts your static igraph object into an engaging model. In this visualization you can scroll in and out to zoom and click and drag to move around the network. It places the network in a spherical shape in the plot window and allows you to move in, out and around it.
graphjs(bali)Now, we can do a little more ere to make the visualization cleaner and more useful. Like working in igraph, either we can set some attributes to the graph directly (like colors etc.) or we can state the options for the alterations when using the graphjs() function. To demonstrate this, we will do a little of both.
First, let’s leave the network visualization pretty basic and just play with some of the parameters of graphjs(). In this, we are going to change the sizes of the nodes, add a title, and set the labels to show the names of the nodes. In this, as you hover over each node the role of the node appears at the top.
graphjs(bali,
vertex.size = 0.5,
vertex.label = V(bali)$role,
bg = "black",
main = "Bali Terrorist Network"
)This is much cleaner than the original visualization. The contrast between the black background and the orange nodes really makes the network pop and reducing the size of the nodes allows you to see the structure much clearer. There is a bit more we can do, however.
Let’s set a color for each role that the individual plays in this network. To do this, we are adding a vertex attribute called “role_color” using a case_when function from dplyr. The logic is pretty straightforward. Take a look.
V(bali)$role_color <- case_when(
V(bali)$role == "CT" ~ "gold",
V(bali)$role == "OA" ~ "green",
V(bali)$role == "BM" ~ "cyan",
V(bali)$role == "SB" ~ "purple",
V(bali)$role == "TL" ~ "orange")
graphjs(bali,
vertex.size = 0.5,
vertex.label = V(bali)$role,
vertex.color = V(bali)$role_color,
edge.color = "white",
bg = "black",
main = "Bali Terrorist Network")Finally, for a little bit of fun, let’s apply some community detection to this network and change the color of the nodes to reflect their community membership. To do this, we can run a louvain community detection algorithm and adding the membership as an attribute to the network as a node characteristic we are calling group.
V(bali)$group <- cluster_louvain(bali)$membership
graphjs(bali,
vertex.size = 0.5,
vertex.label = V(bali)$role,
vertex.color = V(bali)$group,
bg = "white",
main = "Bali Terrorist Network")These 3D networks are really fun and a great way to engage viewers with the network. For more, see https://bwlewis.github.io/rthreejs/.
Next, we cover interactive visualizations that are slightly different from the above 3D networks. There are perks to both methods that become apparent as you use them.
B.3 Interactive visualization with VisNetwork
VisNetwork does not convert your network object into an interactive visualization. Rather, we need to build a specific data set that VisNetwork can pull the edge and node attributes from. To do this, we use the original network object to construct a node characteristics data frame.
nodes <- data.frame(id = V(bali)$vertex.names,
label = V(bali)$role)
edges <- igraph::as_data_frame(bali, what = "edges") |>
mutate(from = V(bali)$vertex.names[from],
to = V(bali)$vertex.names[to]) |>
select(from, to)With this, we can construct our basic interactive network. The basic recipe for this is to use the visNetwork() function and call on the edgelist and node attribute data set. This visualization allows you to select nodes one-by-one and drag them. You will notice that the nodes are “springy.” This is a core feature of the package where it introduces some physics to the network to keep nodes that are tied together close together. We can play around with that in just a moment.
visNetwork(nodes,edges)There is a lot more that we can do with this visualization, however. The chunk below creates a much neater visualization that we can use. It looks like a lot, but this is really just a sequence of small tweaks layered onto a base plot. In short, this process goes from initializing the network, defining the layout, defining groups, improve readability, adding interactivity, to adding legend. Let’s break it down by each option we add so you can get a picture of what it all does.
We start with visNetwork(), which initializes the network and sets the canvas size and background. Next, visIgraphLayout() spaces nodes in a clean, readable way.
Next, visNodes() is used to increase label size to improve readability. visOptions() adds light interactivity by highlighting connections on click while removing the dropdown selector. visPhysics() is used to freeze the layout so nodes don’t keep moving. You can drag and move the nodes around to place them where you want.
visNetwork(nodes,
edges,
width = "100%",
height = "800px",
background = "white") |>
visIgraphLayout(layout = "layout_nicely") |>
visNodes(font = list(size = 30)) |>
visOptions(highlightNearest = TRUE,
nodesIdSelection = FALSE) |>
visPhysics(enabled = FALSE)Now we have that base, we are going to add a few extra cool things to it! We can add back some physics to the visualization, This introduces some repulsion back to the nodes reintroducing that ‘springy’ effect to the graph as you move the nodes. Rather than being able to place nodes where you want, this new network causes nodes to bound back into place after you drag it. However, you will notice that the additional options we introduce make for a little less strong ‘spring’ than the stock options.
visNetwork(nodes,
edges,
width = "100%",
height = "800px",
background = "white") |>
visNodes(font = list(size = 30)) |>
visOptions(highlightNearest = TRUE,
nodesIdSelection = TRUE) |>
visPhysics(solver = "repulsion",
repulsion = list(nodeDistance = 100,
centralGravity = 0.1,
springLength = 200,
springConstant = 0.02,
damping = 0.09),
stabilization = list(enabled = TRUE, iterations = 100))Here is a slightly different alteration of the physics with a slightly different layout, and we can add some control buttons directly to the visualization so that users can navigate the visualization by clicking not dragging their mouse. Finally, to mimic the community detection visualization that we did above, we add an grouping attribute to the vertex data set that pulls the clustering membership from the same louvain method we used in our 3D network. The result, shows a color for each of the identified clusters in the network.
nodes$group <- as.factor(cluster_louvain(bali)$membership)
visNetwork(nodes,
edges,
width = "100%",
height = "800px",
background = "white") |>
visInteraction(navigationButtons = TRUE) |>
visNodes(font = list(size = 30)) |>
visOptions(highlightNearest = TRUE,
nodesIdSelection = TRUE) |>
visPhysics(solver = "forceAtlas2Based",
forceAtlas2Based = list(gravitationalConstant = -50,
centralGravity = 0.01,
springLength = 200,
springConstant = 0.05,
damping = 0.4),
stabilization = list(enabled = TRUE))B.4 Saving Advanced Network visualizations
Since these types of visualizations are not static, we can’t simply save them to a jpeg or png file. Instead, they need to be stored in a file that will open and permit the interactivity. As such, we save them to an .html file that you can open in your broswer.
Here we use a combination of functions from a couple of html-friendly packages. Note that this process applies for both packages. Basically, what this does is ensure that the visualization can be opened in a browser, then saves it to your working directory as a html file and opens it in your default browser.
# Store it
bali_3d <- graphjs(bali,
vertex.size = 0.5,
vertex.label = V(bali)$role,
vertex.color = V(bali)$group,
bg = "white",
main = "Bali Terrorist Network")
# Make is browsable
bali_3d <- browsable(bali_3d)
# Save it
saveWidget(bali_3d, file="bali_3d.html")
# Open it
browseURL("bali_3d.html")For a more detailed tutorial on visNetwork, see https://cran.r-project.org/web/packages/visNetwork/vignettes/Introduction-to-visNetwork.html.