   All posts

## Network Visualisation with visNetwork 22 Dec 2018
Hello, welcome to this post! Today we are talking about networks (also called graphs)!

I will show how to visualise a network using `visNetwork`, which is an R package designed specifically for network visualisation using the vis.js JavaScript library.

visNetwork package is a great R package for network visualisation, and can be used with Shiny and R Markdown, and is fully interactive within the RStudio viewer (more on that later!). We’ll start off building a simple visualisation, and build up to something more complex where we style parts of our network using CSS, add more interactivity and create groupings of nodes. This post assumes no knowledge of `visNetwork`, though knowledge of R, RStudio and networks is useful! You can find all the code, output and more at the post GitHub repository and you can find me on Twitter @stevo_marky.

### Part 1: A Simple Network

A network is a collection of nodes (a point of intersection) and edges (links between the nodes).
The `visNetwork()` function requires a data frame for the nodes of the network and a data frame for the edges between the nodes.

We’re going to start off by building a simple eight node data frame with 15 edges.

We start by creating a simple nodes data frame. To do this we run the following code:
``# create nodes dataframe# eight nodes where nodes have the id 1 to 8# the node labels are Node i where i is between 1 and 8nodes <- data.frame(id = 1:8,                    label = paste("Node", 1:8),                    # make the nodes circle                    shape = "circle")``

We now want to create our edges data frame. To generate the edges, I am going to randomly select 15 connections between the eight nodes. To do this, we perform a neat little trick of getting all the possible combinations of node connections (with the exception of a node connection to itself) and then randomly selecting 15 rows from the resulting data frame.

``library(dplyr)# create two dataframes, one to eight in the first column and a column of 1's in the second column# the second column is for the joinx <- data.frame(edges = 1:8, z = 1)y <- data.frame(edges = 1:8, z = 1)# set seed so the output is reproducibleset.seed(123)# perform a cross join using dplyr::inner_join() and join by z which always matchessample <- x %>%   inner_join(., y, by = 'z') %>%  select(-z) %>%  # filter out any occurrences where nodes are joined to themselves  filter(edges.x != edges.y) %>%   # randomly sample 15 rows from the dataframe  sample_n(., 15) %>%   # these will be the edges in our network  # add additional column for length of edge  mutate(length = round(rnorm(15, mean = 10, sd = 3), 0)) ``

The last line of code above adds an extra column to the data frame and fills it with values from a normal distribution (using `rnorm()`). For now, we will not use this column, but it will come in useful later on!

These fifteen rows are our edges in our network. To create our edges in our network visualisation we first create a data frame using the following code:
``# create edges dataframeedges <- data.frame(from = sample\$edges.x, to = sample\$edges.y)# from and to use a line of the dataframe # an edge is created on a row basis which means that an edge is created from the node number in the edges.x column to the node number in the edges.y column``

Edges are created on a row basis. This means that we have an edge from node 3 to node 4, from node 7 to node 2, from node 4 to node 2 and so on.

``head(sample)``

`    edges.x edges.y length1       3       4     142       7       2      53       4       2     154       7       5     125       7       8     186       1       4     12`

Now we have our `nodes` and `edges` data frame, we are ready to create our first network.

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges) %>%   # use visLayout randomSeed so we always have the same network  visLayout(randomSeed = 123) ``

We now have our network! ### Part 2: Adding Detail to Our Network

We now build on the simple network we created in Part 1. We are going to make our network more aesthetically pleasing. We will build up our network visualisation bit by bit, starting with the nodes.

For our `nodes` data frame, we remove the `shape` argument (that will be moved elsewhere), but instead add a `font.color` argument, where we use the hex code for white.

``# nodesnodes <- data.frame(id = 1:8,                    label = paste("Node", 1:8),                    font.color = "#ffffff") # make font color white``

The only change to our `edges` data frame is adding the length of the edges. Remember the length column we created in our `sample` data frame? Now we specify this column as the label.

``# edgesedges <- data.frame(from = sample\$edges.x,                     to = sample\$edges.y,                    # add the length of the edge to the network                    label = sample\$length) ``

The next stage is where we do the bulk of the work, so let’s break it down. We start with what we had in Part 1:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges) %>%   visLayout(randomSeed = 123)``

The first thing we do is add a title to our network. To do this we use `main =` and pass a list. This list contains our title (`text =`) and some styling (`style =`). Here we use CSS to style the title as we wish. If you're not familiar with CSS, I recommend looking at W3Schools, which is a fantastic resource.

At the moment our code looks like:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges,           main = list(text = "Simple Network with Eight Nodes",                       # style the title using inline css                       style = 'font-family: "Lucida Sans Unicode",                        "Lucida Grande",  sans-serif;                        font-weight: bold;                        font-size: 15px;                        text-align:center')) %>%   # for styling see https://www.w3schools.com/css/default.asp  visLayout(randomSeed = 123)``

We now add what are termed as Global Options in the `visNetwork` package. Global Options are a simple way to set a configuration for all nodes and/or edges.

For nodes, we want to ensure they are all circle (which we moved from our nodes data frame) and make the nodes a dark blue. Therefore, we add `visNodes()`:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges,           main = list(text = "Simple Network with Eight Nodes",                       style = 'font-family: "Lucida Sans Unicode",                        "Lucida Grande",  sans-serif;                        font-weight: bold;                        font-size: 15px;                        text-align:center')) %>%  # use global option for nodes  visNodes(shape = "circle", # move circle to a global option           color = "#0000FF") %>% # make nodes blue  visLayout(randomSeed = 123)``

For edges we want to:

1. make all our edges dashes.

2. make all the edges directional “to” arrows.

3. colour our edges light blue

Adding this to our code block gives us:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges,           main = list(text = "Simple Network with Eight Nodes",                       style = 'font-family: "Lucida Sans Unicode",                        "Lucida Grande",  sans-serif;                        font-weight: bold;                        font-size: 15px;                        text-align:center')) %>%   visNodes(shape = "circle",             color = "#0000FF") %>%    # use global option for edges  visEdges(dashes = TRUE, # add dashes to network to reduce ink           arrows = c("to"), # add directional arrows           color = "#7ec0ee") %>% # make edges light blue  visLayout(randomSeed = 123)``

Our Network now looks much better! Now, we can take advantage of `visNetwork`’s interactivity (I am using RStudio to do this).

We want to add the more interactivity to our network using the RStudio Viewer. We want to make our network visualisation such that when a user clicks on a node, the selected node and the nearest nodes are highlighted. By nearest nodes, we mean those nodes which are connected to our selected node. We also want to highlight only the connections between our selected node and the nodes one connection (one degree ) away. To do this, we add the following code:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges,           main = list(text = "Simple Network with Eight Nodes",                       style = 'font-family: "Lucida Sans Unicode",                        "Lucida Grande",  sans-serif;                        font-weight: bold;                        font-size: 15px;                        text-align:center')) %>%   visNodes(shape = "circle",            color = "#0000FF") %>%   visEdges(dashes = TRUE,            arrows = c("to"),            color = "#7ec0ee") %>%   # on click highlight the nearest nodes  visOptions(highlightNearest = list(enabled = TRUE,   # highlight nodes connected to chosen node                                     degree = 1,   # only look at input/output nodes of the clicked node                                     algorithm = "hierarchical")) %>%   visLayout(randomSeed = 123)``

You’ll notice here that part of the beauty of the `visNetwork` package is being able to use the `magrittr` pipe (`%>%`). We now have a network that is aesthetically pleasing and interactive.

### Part 3: Creating Groups of Nodes

If you look carefully at the network we have created, you’ll see that node 2 and node 6 have no outwards edges, only inwards edges. This is a sink node. If we were travelling around our network and arrived at either node 2 or node 6, we would be stuck, as there is no outward edge. This differentiates these two nodes from all other nodes in our network. We can therefore group our nodes into two distinct groups:

1. Group S: nodes 2 and nodes 6 (S stands for sink).

2. Group A: every remaining node.

The first thing we need to do is amend the `nodes` data frame and add a column which specifies which group each node is in.

``# nodesnodes <- data.frame(id = 1:8,                    # add a column specifying the group                    group = c("A", "S", "A", "A", "A", "S", "A", "A"),                     label = paste("Node", 1:8),                    font.color = "#ffffff")``

Our edges data frame remains the same as before, which is:

``# edgesedges <- data.frame(from = sample\$edges.x,                     to = sample\$edges.y,                    label = sample\$length) ``

We then need to make some changes to our `visNetwork()` call. We firstly remove our `visNodes()` function, as this will be superseded by `visGroups()`. We add a `visGroups()` function for each of our desired groups.

For Group S, we want a special icon for the node. As the fantastic team behind `visNetwork` kindly built `visNetwork` so that it supports Font Awesome , let’s use a Font Awesome icon. To do this, we specify `shape = icon`, and then we pass a list of options:

1. the code of the icon we want (this can be found on the Font Awesome website).

2. the colour, here burnt orange.

3. the size, here let’s make it slightly smaller than the standard node size.

We also need to add `addFontAwesome()` at the bottom of our code for the Font Awesome icons to render.

``# creates groups of nodes# group s  visGroups(groupname = "S", # specify group name            shape = "icon", # use a font awesome icon (https://fontawesome.com/)            # specify icon and colour            icon = list(code = "f057", color = "#cc5500", size = 40)) %>%   addFontAwesome() # add to ensure font awesome icons render``

The code for the Group A nodes is:

``# group avisGroups(groupname = "A",             color = "#0000FF",             shape = "circle")``

Putting all this together gives us:

``library(visNetwork)# create networkvisNetwork(nodes = nodes, edges = edges,           main = list(text = "Simple Network with Eight Nodes",                       style = 'font-family: "Lucida Sans Unicode",                        "Lucida Grande",  sans-serif;                        font-weight: bold;                        font-size: 15px;                        text-align:center')) %>%   visGroups(groupname = "S",             shape = "icon",             icon = list(code = "f057", color = "#cc5500", size = 40)) %>%   visGroups(groupname = "A",             color = "#0000FF",             shape = "circle") %>%   visEdges(dashes = TRUE,            arrows = c("to"),            color = "#7ec0ee") %>%   visOptions(highlightNearest = list(enabled = TRUE,                                      degree = 1,                                      algorithm = "hierarchical")) %>%   visLayout(randomSeed = 123) %>%   addFontAwesome() ``

### Conclusion

Hopefully you have found this post useful! We have shown how to create a simple network, visualise it using `visNetwork`, style the visualisation, add further interactivity, create groups of nodes and add icons to your network. Network visualisation is a fantastic area and an area that is going to grow; we’ve only scratched the surface here using `visNetwork`! The visNetwork documentation shows the breadth of capability of `visNetwork`. If you want advanced network visualisation, I recommend delving into the docs.

If you see any errors in the code, please let me know. If you have any comments, then please leave them below, and don't forget that you can find all the code for this post (and more!) at the post Github repository.

### Session and Package Information

The details of my R session and packages installed are:

``sessionInfo()``

R version 3.5.2 (2018-12-20)
Platform: x86_64-apple-darwin15.6.0 (64-bit)
Running under: macOS High Sierra 10.13.6

attached base packages:
 stats graphics grDevices utils datasets methods base

other attached packages:
 bindrcpp_0.2.2 dplyr_0.7.8 visNetwork_2.0.5