ggplot2
PackageThis vignette is intended to provide several examples of different network visualization methods in the ggplot2
/ “tidyverse” framework. The methods, the geomnet
package, the ggnetwork
package, and the function GGally::ggnet2
, are detailed in the paper Network Visualization with ggplot2
by the authors of this vignette.
library(dplyr)
library(tidyr)
library(ggplot2) # needs to be version ≥ 2.1.0
library(scales)
## ggnet2
if (!require(GGally, quietly = TRUE)) {
getFromNamespace("install_github", asNamespace("devtools"))("ggobi/ggally")
}
##
## Attaching package: 'GGally'
## The following object is masked from 'package:dplyr':
##
## nasa
## geom_net
if (!require(geomnet, quietly = TRUE) ||
packageVersion("geomnet") < "0.2.0") {
getFromNamespace("install_github", asNamespace("devtools"))("sctyner/geomnet")
}
## ggnetwork
if (!require(ggnetwork, quietly = TRUE) ||
packageVersion("ggnetwork") < "0.5.1") {
getFromNamespace("install_github", asNamespace("devtools"))("briatte/ggnetwork")
}
## pre-load
library(network)
## network: Classes for Relational Data
## Version 1.13.0 created on 2015-08-31.
## copyright (c) 2005, Carter T. Butts, University of California-Irvine
## Mark S. Handcock, University of California -- Los Angeles
## David R. Hunter, Penn State University
## Martina Morris, University of Washington
## Skye Bender-deMoll, University of Washington
## For citation information, type citation("network").
## Type help("network-package") to get started.
library(sna)
## Loading required package: statnet.common
## sna: Tools for Social Network Analysis
## Version 2.4 created on 2016-07-23.
## copyright (c) 2005, Carter T. Butts, University of California-Irvine
## For citation information, type citation("sna").
## Type help(package="sna") to get started.
library(GGally)
library(geomnet)
library(ggnetwork)
library(igraph)
##
## Attaching package: 'igraph'
## The following objects are masked from 'package:sna':
##
## betweenness, bonpow, closeness, components, degree,
## dyad.census, evcent, hierarchy, is.connected, neighborhood,
## triad.census
## The following objects are masked from 'package:network':
##
## %c%, %s%, add.edges, add.vertices, delete.edges,
## delete.vertices, get.edge.attribute, get.edges,
## get.vertex.attribute, is.bipartite, is.directed,
## list.edge.attributes, list.vertex.attributes,
## set.edge.attribute, set.vertex.attribute
## The following objects are masked from 'package:tidyr':
##
## %>%, crossing
## The following objects are masked from 'package:dplyr':
##
## %>%, as_data_frame, groups, union
## The following objects are masked from 'package:stats':
##
## decompose, spectrum
## The following object is masked from 'package:base':
##
## union
geomnet
# make data accessible
data(madmen, package = "geomnet")
# code for geom_net
# data step: merge edges and nodes by the "from" column
MMnet <- fortify(as.edgedf(madmen$edges), madmen$vertices)
## Using Name1 as the from node column and Name2 as the to node column.
## If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
## Joining edge and node information by from_id and label respectively.
# create plot
set.seed(10052016)
ggplot(data = MMnet, aes(from_id = from_id, to_id = to_id)) +
geom_net(aes(colour = Gender), layout.alg = "kamadakawai",
size = 2, labelon = TRUE, vjust = -0.6, ecolour = "grey60",
directed =FALSE, fontsize = 3, ealpha = 0.5) +
scale_colour_manual(values = c("#FF69B4", "#0099ff")) +
xlim(c(-0.05, 1.05)) +
theme_net() +
theme(legend.position = "bottom")
Mad Men relationship network included in the gcookbook
package by Winston Chang visualized using geomnet
.
ggnet2
library(GGally)
library(network)
# make the data available
data(madmen, package = 'geomnet')
# data step for both ggnet2 and ggnetwork
# create undirected network
mm.net <- network::network(madmen$edges[, 1:2], directed = FALSE)
# create node attribute (gender)
rownames(madmen$vertices) <- madmen$vertices$label
mm.net %v% "gender" <- as.character(
madmen$vertices[ network.vertex.names(mm.net), "Gender"]
)
# gender color palette
mm.col <- c("female" = "#ff69b4", "male" = "#0099ff")
# create plot for ggnet2
set.seed(10052016)
ggnet2(mm.net, color = mm.col[ mm.net %v% "gender" ],
label = TRUE, label.color = mm.col[ mm.net %v% "gender" ],
size = 2, vjust = -0.6, mode = "kamadakawai", label.size = 3)
Mad Men example using the ggnet2
function in the GGally
package.
ggnetwork
# create plot for ggnetwork. uses same data created for ggnet2 function
library(ggnetwork)
set.seed(10052016)
ggplot(data = ggnetwork(mm.net, layout = "kamadakawai"),
aes(x, y, xend = xend, yend = yend)) +
geom_edges(color = "grey50") +
geom_nodes(aes(colour = gender), size = 2) +
geom_nodetext(aes(colour = gender, label = vertex.names),
size = 3, vjust = -0.6) +
scale_colour_manual(values = mm.col) +
xlim(c(-0.05, 1.05)) +
theme_blank() +
theme(legend.position = "bottom")
Mad Men example using the ggnetwork
package.
Blood donation “network”: which blood types can give and receive?
ggnet2
# make data accessible
data(blood, package = "geomnet")
# plot with ggnet2 (Figure 2a)
set.seed(12252016)
ggnet2(network::network(blood$edges[, 1:2], directed=TRUE),
mode = "circle", size = 15, label = TRUE,
arrow.size = 10, arrow.gap = 0.05, vjust = 0.5,
node.color = "darkred", label.color = "grey80")
ggnet
implementation
geomnet
# plot with geomnet (Figure 2b)
set.seed(12252016)
ggplot(data = blood$edges, aes(from_id = from, to_id = to)) +
geom_net(colour = "darkred", layout.alg = "circle", labelon = TRUE, size = 15,
directed = TRUE, vjust = 0.5, labelcolour = "grey80",
arrowsize = 1.5, linewidth = 0.5, arrowgap = 0.05,
selfloops = TRUE, ecolour = "grey40") +
theme_net()
geom_net
implementation
ggnetwork
# plot with ggnetwork (Figure 2c)
set.seed(12252016)
ggplot(ggnetwork(network::network(blood$edges[, 1:2]),
layout = "circle", arrow.gap = 0.05),
aes(x, y, xend = xend, yend = yend)) +
geom_edges(color = "grey50",
arrow = arrow(length = unit(10, "pt"), type = "closed")) +
geom_nodes(size = 15, color = "darkred") +
geom_nodetext(aes(label = vertex.names), color = "grey80") +
theme_blank()
ggnetwork
implementation
A faux company’s email network provided by the 2014 VAST Challenge.
ggnet2
# make data accessible
data(email, package = 'geomnet')
# create node attribute data
em.cet <- as.character(
email$nodes$CurrentEmploymentType)
names(em.cet) = email$nodes$label
# remove the emails sent to all employees
edges <- subset(email$edges, nrecipients < 54)
# create network
em.net <- edges[, c("From", "to") ]
em.net <- network::network(em.net, directed = TRUE)
# create employee type node attribute
em.net %v% "curr_empl_type" <-
em.cet[ network.vertex.names(em.net) ]
set.seed(10312016)
ggnet2(em.net, color = "curr_empl_type",
size = 4, palette = "Set1",
arrow.size = 5, arrow.gap = 0.02,
edge.alpha = 0.25, mode = "fruchtermanreingold",
edge.color = c("color", "grey50"),
color.legend = "Employment Type") +
theme(legend.position = "bottom")
## Warning: Removed 727 rows containing missing values (geom_segment).
The company’s email network visualized with ggnet2
.
geomnet
# data step for the geomnet plot
email$edges <- email$edges[, c(1,5,2:4,6:9)]
emailnet <- fortify(
as.edgedf(subset(email$edges, nrecipients < 54)),
email$nodes)
## Using From as the from node column and to as the to node column.
## If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
## Joining edge and node information by from_id and label respectively.
set.seed(10312016)
ggplot(data = emailnet,
aes(from_id = from_id, to_id = to_id)) +
geom_net(layout.alg = "fruchtermanreingold",
aes(colour = CurrentEmploymentType,
group = CurrentEmploymentType,
linewidth = 3 * (...samegroup.. / 8 + .125)),
ealpha = 0.25,
size = 4, curvature = 0.05,
directed = TRUE, arrowsize = 0.5) +
scale_colour_brewer("Employment Type", palette = "Set1") +
theme_net() +
theme(legend.position = "bottom")
The company’s email network visualized with geomnet
.
ggnetwork
# use em.net created in ggnet2step
set.seed(10312016)
ggplot(ggnetwork(em.net, arrow.gap = 0.02, layout = "fruchtermanreingold"),
aes(x, y, xend = xend, yend = yend)) +
geom_edges(
aes(color = curr_empl_type),
alpha = 0.25,
arrow = arrow(length = unit(5, "pt"),
type = "closed"),
curvature = 0.05) +
geom_nodes(aes(color = curr_empl_type),
size = 4) +
scale_color_brewer("Employment Type",
palette = "Set1") +
theme_blank() +
theme(legend.position = "bottom")
## Warning in fortify.network(x, ...): duplicated edges detected
The company’s email network visualized with ggnetwork
.
ggnet2
# ggnet2 code for the email network facetted by day as shown in fig.4a
# data preparation
em.day <- subset(email$edges, nrecipients < 54)[, c("From", "to", "day") ]
# create one element in a list per day
em.day <- lapply(unique(em.day$day),
function(x) subset(em.day, day == x)[, 1:2 ])
# create list of networks
em.day <- lapply(em.day, network, directed = TRUE)
# create node (employee type) and network (day) attributes for each element in list
for (i in 1:length(em.day)) {
em.day[[ i ]] %v% "curr_empl_type" <-
em.cet[ network.vertex.names(em.day[[ i ]]) ]
em.day[[ i ]] %n% "day" <- unique(email$edges$day)[ i ]
}
# plot ggnet2
g <- list(length(em.day))
set.seed(7042016)
# plot each element in list
for (i in 1:length(em.day)) {
g[[ i ]] <- ggnet2(em.day[[ i ]], size = 2, color = "curr_empl_type",
palette = "Set1", arrow.size = 0, arrow.gap = 0.01,
edge.alpha = 0.1, legend.position = "none",
mode = "kamadakawai") +
ggtitle(paste("Day", em.day[[ i ]] %n% "day")) +
theme(panel.border = element_rect(color = "grey50", fill = NA),
aspect.ratio = 1)
}
grid.arrange <- getFromNamespace("grid.arrange", asNamespace("gridExtra"))
grid.arrange(grobs = g, nrow = 2)
## Warning: Removed 47 rows containing missing values (geom_segment).
## Warning: Removed 86 rows containing missing values (geom_segment).
## Warning: Removed 71 rows containing missing values (geom_segment).
## Warning: Removed 61 rows containing missing values (geom_segment).
## Warning: Removed 65 rows containing missing values (geom_segment).
## Warning: Removed 76 rows containing missing values (geom_segment).
## Warning: Removed 101 rows containing missing values (geom_segment).
## Warning: Removed 45 rows containing missing values (geom_segment).
## Warning: Removed 82 rows containing missing values (geom_segment).
## Warning: Removed 93 rows containing missing values (geom_segment).
geomnet
# geomnet code for the email network facetted by day as shown in fig.4b
# data step: making sure that there is one entry for each person on each day so that all employees are included in the network even on days they don't send/receive emails
emailnet <- fortify(as.edgedf(subset(email$edges, nrecipients < 54)), email$nodes, group = "day")
## Using From as the from node column and to as the to node column.
## If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
## Joining edge and node information by from_id and label respectively.
# creating the plot
set.seed(7042016)
ggplot(data = emailnet, aes(from_id = from, to_id = to_id)) +
geom_net(layout.alg = "kamadakawai",
aes(colour = CurrentEmploymentType,
group = CurrentEmploymentType,
linewidth = 2 * (...samegroup.. / 8 + .125)),
arrowsize = .5,
directed = TRUE, fiteach = TRUE, ealpha = 0.5, size = 1.5, na.rm = FALSE) +
scale_colour_brewer("Employment Type", palette = "Set1") +
theme_net() +
facet_wrap(~day, nrow = 2, labeller = "label_both") +
theme(legend.position = "bottom",
panel.border = element_rect(fill = NA, colour = "grey60"),
plot.margin = unit(c(0, 0, 0, 0), "mm"))
##
ggnetwork
# ggnetwork code for the email network facetted by day as shown in fig.4c
# create the network and aesthetics
edges <- subset(email$edges, nrecipients < 54)
edges <- edges[, c("From", "to", "day") ]
em.net <- network::network(edges[, 1:2])
# assign edge attributes (day)
network::set.edge.attribute(em.net, "day", edges[, 3])
# assign node attributes (employee type)
em.net %v% "curr_empl_type" <- em.cet[ network.vertex.names(em.net) ]
# create the plot
set.seed(7042016)
ggplot(ggnetwork(em.net, arrow.gap = 0.02, by = "day",
layout = "kamadakawai"),
aes(x, y, xend = xend, yend = yend)) +
geom_edges(
aes(color = curr_empl_type),
alpha = 0.25,
arrow = arrow(length = unit(5, "pt"), type = "closed")) +
geom_nodes(aes(color = curr_empl_type), size = 1.5) +
scale_color_brewer("Employment Type", palette = "Set1") +
facet_wrap(~day, nrow = 2, labeller = "label_both") +
theme_facet(legend.position = "bottom")
## Warning in fortify.network(x, ...): duplicated edges detected
ggplot2
Theme Element Inheritance Networkggnet2
# make data accessible
data(theme_elements, package = "geomnet")
# create network object
te.net <- network::network(theme_elements$edges)
# assign node attribut (size based on node degree)
te.net %v% "size" <-
sqrt(10 * (sna::degree(te.net) + 1))
set.seed(3272016)
ggnet2(te.net, label = TRUE, color = "white", label.size = "size",
mode = "fruchtermanreingold", layout.exp = 0.15)
##
geomnet
# data step: merge nodes and edges and
# introduce a degree-out variable
TEnet <- fortify(as.edgedf(theme_elements$edges[,c("parent", "child")]), theme_elements$vertices)
## Using parent as the from node column and child as the to node column.
## If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
## Joining edge and node information by from_id and name respectively.
TEnet <- TEnet %>%
group_by(from_id) %>%
mutate(degree = sqrt(10 * n() + 1))
# create plot:
set.seed(3272016)
ggplot(data = TEnet,
aes(from_id = from_id, to_id = to_id)) +
geom_net(layout.alg = "fruchtermanreingold",
aes(fontsize = degree), directed = TRUE,
labelon = TRUE, size = 1, labelcolour = 'black',
ecolour = "grey70", arrowsize = 0.5,
linewidth = 0.5, repel = TRUE) +
theme_net() +
xlim(c(-0.05, 1.05))
ggnetwork
set.seed(3272016)
# use network created in ggnet2 data step
ggplot(ggnetwork(te.net, layout = "fruchtermanreingold"),
aes(x, y, xend = xend, yend = yend)) +
geom_edges() +
geom_nodes(size = 12, color = "white") +
geom_nodetext(
aes(size = size, label = vertex.names)) +
scale_size_continuous(range = c(4, 8)) +
guides(size = FALSE) +
theme_blank()
ggnet2
#make data accessible
data(football, package = 'geomnet')
rownames(football$vertices) <-
football$vertices$label
# create network
fb.net <- network::network(football$edges[, 1:2],
directed = TRUE)
# create node attribute (what conference is team in?)
fb.net %v% "conf" <-
football$vertices[
network.vertex.names(fb.net), "value"
]
# create edge attribute (between teams in same conference?)
network::set.edge.attribute(
fb.net, "same.conf",
football$edges$same.conf)
set.seed(5232011)
ggnet2(fb.net, mode = "fruchtermanreingold",
color = "conf", palette = "Paired",
color.legend = "Conference",
edge.color = c("color", "grey75"))
geomnet
# data step: merge vertices and edges
ftnet <- fortify(as.edgedf(football$edges), football$vertices)
## Using from as the from node column and to as the to node column.
## If this is not correct, rewrite dat so that the first 2 columns are from and to node, respectively.
## Joining edge and node information by from_id and label respectively.
# create new label variable for viewing independent schools
ftnet$schools <- ifelse(
ftnet$value == "Independents", ftnet$from_id, "")
# create data plot
set.seed(5232011)
ggplot(data = ftnet,
aes(from_id = from_id, to_id = to_id)) +
geom_net(layout.alg = 'fruchtermanreingold',
aes(colour = value, group = value,
linetype = factor(same.conf != 1),
label = schools),
linewidth = 0.5,
size = 5, vjust = -0.75, alpha = 0.3) +
theme_net() +
theme(legend.position = "bottom") +
scale_colour_brewer("Conference", palette = "Paired") +
guides(linetype = FALSE)
ggnetwork
# use network from ggnet2 step
set.seed(5232011)
ggplot(
ggnetwork(
fb.net,
layout = "fruchtermanreingold"),
aes(x, y, xend = xend, yend = yend)) +
geom_edges(
aes(linetype = as.factor(same.conf)),
color = "grey50") +
geom_nodes(aes(color = conf), size = 4) +
scale_color_brewer("Conference",
palette = "Paired") +
scale_linetype_manual(values = c(2,1)) +
guides(linetype = FALSE) +
theme_blank()
# access the data and rename it for convenience
library(tnet)
## Loading required package: survival
## tnet: Analysis of Weighted, Two-mode, and Longitudinal networks.
## Type ?tnet for help.
data(tnet)
elist <- data.frame(Davis.Southern.women.2mode)
names(elist) <- c("Lady", "Event")
detach(package:tnet)
detach(package:igraph)
head(elist)
## Lady Event
## 1 1 1
## 2 1 2
## 3 1 3
## 4 1 4
## 5 1 5
## 6 1 6
elist$Lady <- paste("L", elist$Lady, sep="")
elist$Event <- paste("E", elist$Event, sep="")
davis <- elist
names(davis) <- c("from", "to")
davis <- rbind(davis, data.frame(from=davis$to, to=davis$from))
davis$type <- factor(c(rep("Lady", nrow(elist)), rep("Event", nrow(elist))))
ggnet2
# Southern women network in ggnet2
# create affiliation matrix
bip = xtabs(~Event+Lady, data=elist)
# weighted bipartite network
bip = network::network(bip,
matrix.type = "bipartite",
ignore.eval = FALSE,
names.eval = "weights")
# detect and color the mode
#set.seed(8262013)
#ggnet2(bip, color = "mode", palette = "Set2",
# shape = "mode", mode = "kamadakawai",
# size = 15, label = TRUE) +
# theme(legend.position="bottom")
geomnet
# Southern women network in geomnet
# change labelcolour
davis$lcolour <-
c("white", "black")[as.numeric(davis$type)]
set.seed(8262013)
ggplot(data = davis) +
geom_net(layout.alg = "kamadakawai",
aes(from_id = from, to_id = to,
colour = type, shape = type),
size = 15, labelon = TRUE, ealpha = 0.25,
vjust = 0.5, hjust = 0.5,
labelcolour = davis$lcolour) +
theme_net() +
scale_colour_brewer("Type of node", palette = "Set2") +
scale_shape("Type of node") +
theme(legend.position = "bottom")
ggnetwork
# Southern women network in ggnetwork. Use data from ggnet2 step
# assign vertex attributes (Node type and label)
network::set.vertex.attribute(bip, "mode",
c(rep("event", 14), rep("woman", 18)))
set.seed(8262013)
ggplot(data = ggnetwork(bip, layout = "kamadakawai"),
aes(x = x, y = y, xend = xend, yend = yend)) +
geom_edges(colour = "grey80") +
geom_nodes(aes(colour = mode, shape = mode), size = 15) +
geom_nodetext(aes(label = vertex.names)) +
scale_colour_brewer(palette = "Set2") +
theme_blank() +
theme(legend.position = "bottom")
See the “Speed comparisons of graph drawing packages” vignette for all the necessary details.