A network is a series of connections. We get networks when things (nodes) relate to each other (edges).
This is the visNetwork package:
library(visNetwork)
It allows us to
data.frame objects.Let’s make a random network. We will have
# Set seed for reproducibility
set.seed(1234567890)
# The sixteen nodes
population <- 1:16
# Network relationships
relate_fr <- rep(population, each = 2) # from
relate_to <- sample(population, 32, T) # to
nodes <- data.frame(
id = population,
label = paste0("N", population)
)
edges <- data.frame(
from = relate_fr,
to = relate_to
)
visNetwork(nodes, edges) %>%
visNodes(shape = "ellipse") %>%
visEdges(color = "black")
Note that edges and the relationships they describe can have direction. Edges with a direction are called directed edges.
For example, think of a social network where
Life is very complicated.
nodes <- data.frame(
id = c(1, 2, 3),
label = c(":(", ":(", ":("),
value = c(10, 10, 10)
)
edges <- data.frame(
from = c(1, 2, 3),
to = c(2, 3, 1),
label = "fancies"
)
visNetwork(nodes, edges) %>%
visNodes(shape = "ellipse") %>%
visEdges(arrows = "to", smooth = list(enabled = FALSE))
Consider this figure from a recently published paper that I was involved in (Froese et al., 2019):
Bayesian network for risk of invasive weed spread
We’re going to make this plot. (Only the first one (a), though).
First note the structure of the network
The main structure for the network is given by the nodes
We will set this up first.
basic_network <- data.frame(
from = c(1, 2, 3, 4),
to = c(3, 3, 5, 5),
arrows = "to"
)
vertex_info <- data.frame(
id = 1:5,
label = c(
"Establishment",
"Persistence",
"Suitability",
"Propagule\npressure",
"Susceptibility"
),
value = 1:5,
group = "Not user-specified", # for the legend
shape = rep("box", 5), # shape
color = "black", # color
font.color = "white", # text color in boxes when labelling
shadow = rep(TRUE, 5)
)
Now for a vibe check:
visNetwork(vertex_info, basic_network) %>%
visNodes(shape = "ellipse") %>%
visEdges(arrows = "to", smooth = list(enabled = FALSE))
The basic structure is fine though the random layout of the graph can be a bit annoying.
Now we can add in the parent nodes. This part gets a bit messy, so hold on.
Firstly, note that the colour of the nodes in the original plot can be red, yellow or green. This is a traffic-light system that weights the contribution of each parent node to the child node. The following functions are used to automate the process of assigning colours to numbers. This won’t be a part of every set up.
colour_labeller <- function(wt) switch(
wt, "1" = "forestgreen", "2" = "orange", "3" = "red"
)
colour_labeller_vectorised <- function(wts){
if(!all(wts %in% 1:3)){
stop("All weights must be 1, 2, or 3")
}
unlist(
lapply(wts, colour_labeller)
)
}
Secondly, we set up the new parent nodes in a different data.frame object. We will join them later.
# The weights and names of nodes
establishment <- c("SoilMoisture", "CanopyCover", "VegetationLandUse")
establishment_wts <- c(1, 2, 3)
persistence <- c("Elevation", "RainDriestMonth")
persistence_wts <- c(1, 2)
propagule <- c("PropaguleSupply", "Hydrochory", "Zoochory")
propagule_wts <- c(3, 2, 3)
# Assign ids
n_est <- length(establishment)
n_per <- length(persistence)
n_prg <- length(propagule)
est_id <- 6:(5 + n_est)
per_id <- (max(est_id) + 1):(max(est_id) + n_per)
prg_id <- (max(per_id) + 1):(max(per_id) + n_prg)
# Set up new data frame of edges
new_connections <- data.frame(
from = c(est_id, per_id, prg_id),
to = c(rep(1, n_est), rep(2, n_per), rep(4, n_prg)),
arrows = "to"
)
# Set up new frame for vertex information
n_elem <- length(c(establishment, persistence, propagule))
new_vertex_info <- data.frame(
id = c(est_id, per_id, prg_id),
label = c(establishment, persistence, propagule),
value = c(est_id, per_id, prg_id),
group = paste("Weight =",
c(establishment_wts, persistence_wts, propagule_wts)
), # for the legend
shape = rep("ellipse", n_elem),
color = colour_labeller_vectorised(
c(establishment_wts, persistence_wts, propagule_wts)
), # colors!
font.color = "white",
shadow = rep(FALSE, n_elem)
)
Thirdly, we join the new node/edge data.frame objects to the old ones.
all_network <- rbind(
basic_network,
new_connections
)
all_vertex <- rbind(
vertex_info,
new_vertex_info
)
Finally, we create the plot:
the_graph <- visNetwork(
all_vertex,
all_network
) %>%
# Constrain edges to be straight
visEdges(physics = FALSE, smooth = FALSE) %>%
# Set up legend entries for the groups
visGroups(groupname = "Weight = 1", color = "forestgreen", font = list(color = "white")) %>%
visGroups(groupname = "Weight = 2", color = "orange", font = list(color = "white")) %>%
visGroups(groupname = "Weight = 3", color = "red", font = list(color = "white")) %>%
visGroups(groupname = "Not user-specified", color = "black", shape = "box", font = list(color = "white")) %>%
# Create legend with title 'Legend'
visLegend(main = "Legend") %>%
visPhysics(enabled = FALSE) %>%
visLayout(randomSeed = 7000)
the_graph
Froese, J.G., Pearse, A.R. & Hamilton, G.S. (2019). Rapid spatial risk modelling for management of early weed invasions: Balancing ecological complexity and operational needs. Methods in Ecology and Evolution, 0(0),1-13.