igraph Network Visualization: Datasets 1 & 2
Examples pulled from Network Visualization with R, Polnet 2016 by Katya Ognyanova.
1. igraph Visualization with Dataset 1
suppressPackageStartupMessages({
library(RColorBrewer)
library(readr)
library(dplyr)
library(igraph)
})
data_dir <- "Data files"
For igraph plotting parameters, node options start with vertex. and edge options start with edge.. Get options with
?igraph.plotting
Set node & edge options in two ways:
- specify them in
plot()function - add them to the
igraphobject
Basic igraph with Dataset 1
nodes <-
read_csv(file.path(data_dir,
"Dataset1-Media-Example-NODES.csv"))
links <-
read_csv(file.path(data_dir,
"Dataset1-Media-Example-EDGES.csv")) %>%
group_by(from,to,type) %>%
summarize(weight = sum(weight)) %>%
ungroup %>%
arrange(from,to)
Convert dataset 1 to an igraph object. The graph_from_data_frame() function takes two data frames, d (edges of the network) and vertices (nodes). d has (at least) two columns with source and target node IDs for each network tie. vertices has (at least) a column of node IDs. Additional columns in either data frame are interpreted as attributes.
(net <- graph_from_data_frame(d=links, vertices=nodes, directed=T))
## IGRAPH 73ff3df DNW- 17 49 --
## + attr: name (v/c), media (v/c), media.type (v/n), type.label (v/c),
## | audience.size (v/n), type (e/c), weight (e/n)
## + edges from 73ff3df (vertex names):
## [1] s01->s02 s01->s03 s01->s04 s01->s15 s02->s01 s02->s03 s02->s09 s02->s10 s03->s01
## [10] s03->s04 s03->s05 s03->s08 s03->s10 s03->s11 s03->s12 s04->s03 s04->s06 s04->s11
## [19] s04->s12 s04->s17 s05->s01 s05->s02 s05->s09 s05->s15 s06->s06 s06->s16 s06->s17
## [28] s07->s03 s07->s08 s07->s10 s07->s14 s08->s03 s08->s07 s08->s09 s09->s10 s10->s03
## [37] s12->s06 s12->s13 s12->s14 s13->s12 s13->s17 s14->s11 s14->s13 s15->s01 s15->s04
## [46] s15->s06 s16->s06 s16->s17 s17->s04
Access nodes, edges, and attributes:
E(net)
## + 49/49 edges from 73ff3df (vertex names):
## [1] s01->s02 s01->s03 s01->s04 s01->s15 s02->s01 s02->s03 s02->s09 s02->s10 s03->s01
## [10] s03->s04 s03->s05 s03->s08 s03->s10 s03->s11 s03->s12 s04->s03 s04->s06 s04->s11
## [19] s04->s12 s04->s17 s05->s01 s05->s02 s05->s09 s05->s15 s06->s06 s06->s16 s06->s17
## [28] s07->s03 s07->s08 s07->s10 s07->s14 s08->s03 s08->s07 s08->s09 s09->s10 s10->s03
## [37] s12->s06 s12->s13 s12->s14 s13->s12 s13->s17 s14->s11 s14->s13 s15->s01 s15->s04
## [46] s15->s06 s16->s06 s16->s17 s17->s04
V(net)
## + 17/17 vertices, named, from 73ff3df:
## [1] s01 s02 s03 s04 s05 s06 s07 s08 s09 s10 s11 s12 s13 s14 s15 s16 s17
E(net)$type
## [1] "hyperlink" "hyperlink" "hyperlink" "mention" "hyperlink" "hyperlink" "hyperlink"
## [8] "hyperlink" "hyperlink" "hyperlink" "hyperlink" "hyperlink" "mention" "hyperlink"
## [15] "hyperlink" "hyperlink" "mention" "mention" "hyperlink" "mention" "mention"
## [22] "hyperlink" "hyperlink" "mention" "hyperlink" "hyperlink" "mention" "mention"
## [29] "mention" "hyperlink" "mention" "hyperlink" "mention" "mention" "mention"
## [36] "hyperlink" "mention" "hyperlink" "mention" "hyperlink" "mention" "mention"
## [43] "mention" "hyperlink" "hyperlink" "hyperlink" "hyperlink" "mention" "hyperlink"
V(net)$media
## [1] "NY Times" "Washington Post" "Wall Street Journal" "USA Today"
## [5] "LA Times" "New York Post" "CNN" "MSNBC"
## [9] "FOX News" "ABC" "BBC" "Yahoo News"
## [13] "Google News" "Reuters.com" "NYTimes.com" "WashingtonPost.com"
## [17] "AOL.com"
Find specific nodes and edges by attribute
V(net)[media=="BBC"]
## + 1/17 vertex, named, from 73ff3df:
## [1] s11
E(net)[type=="mention"]
## + 20/49 edges from 73ff3df (vertex names):
## [1] s01->s15 s03->s10 s04->s06 s04->s11 s04->s17 s05->s01 s05->s15 s06->s17 s07->s03
## [10] s07->s08 s07->s14 s08->s07 s08->s09 s09->s10 s12->s06 s12->s14 s13->s17 s14->s11
## [19] s14->s13 s16->s17
Extract an edge list or matrix from igraph networks.
tbl_df(as_edgelist(net, names=T))
## Error:
## ! `tbl_df()` was deprecated in dplyr 1.0.0 and is now defunct.
## ℹ Please use `tibble::as_tibble()` instead.
as_adjacency_matrix(net, attr="weight")
## 17 x 17 sparse Matrix of class "dgCMatrix"
##
## s01 . 22 22 21 . . . . . . . . . . 20 . .
## s02 23 . 21 . . . . . 1 5 . . . . . . .
## s03 21 . . 22 1 . . 4 . 2 1 1 . . . . .
## s04 . . 23 . . 1 . . . . 22 3 . . . . 2
## s05 1 21 . . . . . . 2 . . . . . 21 . .
## s06 . . . . . 1 . . . . . . . . . 21 21
## s07 . . 1 . . . . 22 . 21 . . . 4 . . .
## s08 . . 2 . . . 21 . 23 . . . . . . . .
## s09 . . . . . . . . . 21 . . . . . . .
## s10 . . 2 . . . . . . . . . . . . . .
## s11 . . . . . . . . . . . . . . . . .
## s12 . . . . . 2 . . . . . . 22 22 . . .
## s13 . . . . . . . . . . . 21 . . . . 1
## s14 . . . . . . . . . . 1 . 21 . . . .
## s15 22 . . 1 . 4 . . . . . . . . . . .
## s16 . . . . . 23 . . . . . . . . . . 21
## s17 . . . 4 . . . . . . . . . . . . .
Look at network matrix directly:
net[1,]
## s01 s02 s03 s04 s05 s06 s07 s08 s09 s10 s11 s12 s13 s14 s15 s16 s17
## 0 22 22 21 0 0 0 0 0 0 0 0 0 0 20 0 0
net[5,7]
## [1] 0
Plot graph, removing loops and labels, and reducing arrow size.
net <- simplify(net, remove.multiple = F, remove.loops = T)
plot(net,
edge.arrow.size = 0.4,
vertex.label = NA)
Annotating a graph
Using options to plot
Plot with curved edges (edge.curved=.1) and reduce arrow size. Using curved edges reveals multiple links between two nodes (e.g. links going in either direction, or multiplex links).
plot(net, edge.arrow.size=.4, edge.curved=.1)
Set node color to orange and the border color to hex #555555. Replace the vertex label with the node names stored in media.
plot(net, edge.arrow.size=.4, edge.curved=0,
vertex.color="orange", vertex.frame.color="#555555",
vertex.label=V(net)$media, vertex.label.color="black",
vertex.label.cex=.7)
Semantic network with only node labels.
plot(net, vertex.shape="none", vertex.label=V(net)$media,
vertex.label.font=2, vertex.label.color="gray40",
vertex.label.cex=0.7, edge.color="gray90")
Using graph attributes
# Generate colors based on media type:
colrs <- c("gray50", "tomato", "gold")
V(net)$color <- colrs[V(net)$media.type]
# Compute node degree (#links) and use it to set node size:
deg <- degree(net, mode="all")
V(net)$size <- deg*3
V(net)$size <- V(net)$audience.size*0.6
# The labels are currently node IDs.
# Setting them to NA will render no labels:
V(net)$label.color <- "black"
V(net)$label <- NA
# Set edge width based on weight:
E(net)$width <- E(net)$weight/6
#change arrow size and edge color:
E(net)$arrow.size <- .2
E(net)$edge.color <- "gray80"
plot(net)
Override attributes explicitly in the plot
plot(net, edge.color="orange", vertex.color="gray50")
Legend
Add legend explaining color meaning.
x,y: legend coordinatespch: element symbolpt.bg: background colorcol: border color,pt.cex: symbol sizebty: type of box around legendncol: number of columns for legend
plot(net)
legend(x = -1.1, y = -1.1,
c("Newspaper","Television", "Online News"),
pch = 21,
col = "#777777", pt.bg = colrs,
pt.cex = 2.5,
bty = "n", ncol = 1)
Highlight Graph Aspects
Color graph edges based on source node color. Here ends() gives us the start and end for each edge in es, and names controls whether ends() will return node names or IDs.
edge.start <- ends(net, es=E(net), names=F)[,1] # get the "from" node
edge.col <- V(net)$color[edge.start]
plot(net, edge.color = edge.col, edge.curved = .1)
Keep only edges with weight higher than network mean using delete_edges(net, edges).
cut.off <- mean(links$weight)
net.sp <- delete_edges(net, E(net)[weight<cut.off])
plot(net.sp)
Plot two tie types (hyperlink and mention) separately.
E(net)$width <- 1.5
net.m <- net - E(net)[E(net)$type=="hyperlink"] # another way to delete edges:
net.h <- net - E(net)[E(net)$type=="mention"] # using the minus operator
par(mfrow=c(1,2))
plot(net.h, vertex.color="orange", main="Tie: Hyperlink")
plot(net.m, vertex.color="lightsteelblue2", main="Tie: Mention")
Force nodes to stay in place for both plots.
l <- layout_with_fr(net)
par(mfrow=c(1,2))
plot(net.h, vertex.color="orange", layout=l, main="Tie: Hyperlink")
plot(net.m, vertex.color="lightsteelblue2", layout=l, main="Tie: Mention")
Show network communities.
par(mfrow=c(1,2))
# Community detection based on label propagation:
clp <- cluster_label_prop(net)
class(clp)
## [1] "communities"
# Community detection returns an object of class "communities"
# which igraph knows how to plot:
plot(clp, net)
# We can also plot the communities without relying on their built-in plot:
V(net)$community <- clp$membership
colrs <- adjustcolor( c("gray50", "tomato", "gold", "yellowgreen"), alpha=.6)
plot(net, vertex.color=colrs[V(net)$community])
Highlight Distance to Node(s)
Focus on a particular node or group of nodes. In the example media network, examine spread of information from the NYT.
The distances() function returns a matrix of shortest paths from nodes listed in the v parameter to ones included in the to parameter.
(dist.from.NYT <- distances(net, v=V(net)[media=="NY Times"],
to=V(net), weights=NA))
## s01 s02 s03 s04 s05 s06 s07 s08 s09 s10 s11 s12 s13 s14 s15 s16 s17
## s01 0 1 1 1 1 2 2 2 2 2 2 2 3 3 1 3 2
Set colors to plot the distances
oranges <- colorRampPalette(c("dark red", "gold"))
col <- oranges(max(dist.from.NYT) + 1)
col <- col[dist.from.NYT + 1]
plot(net, vertex.color = col,
vertex.label = dist.from.NYT,
edge.arrow.size = .6,
vertex.label.color = "white")
Highlight a network path
(news.path <- shortest_paths(net,
from = V(net)[media=="MSNBC"],
to = V(net)[media=="New York Post"],
output = "both")) # both path nodes and edges
## $vpath
## $vpath[[1]]
## + 4/17 vertices, named, from 0aa98db:
## [1] s08 s03 s12 s06
##
##
## $epath
## $epath[[1]]
## + 3/48 edges from 0aa98db (vertex names):
## [1] s08->s03 s03->s12 s12->s06
##
##
## $predecessors
## NULL
##
## $inbound_edges
## NULL
Generate edge color and width and node color variables to plot path. Awkward, but it works.
ecol <- rep("gray80", ecount(net))
ecol[unlist(news.path$epath)] <- "orange"
ew <- rep(2, ecount(net))
ew[unlist(news.path$epath)] <- 4
vcol <- rep("gray40", vcount(net))
vcol[unlist(news.path$vpath)] <- "gold"
plot(net,
vertex.color = vcol,
edge.color = ecol,
edge.width = ew,
edge.arrow.mode = 0)
Highlight edges of a vertex, for instance the WSJ. For a single node, use incident(), for multiple nodes use incident_edges().
(inc.edges <- incident(net, V(net)[media=="Wall Street Journal"], mode="all"))
## + 13/48 edges from 0aa98db (vertex names):
## [1] s03->s01 s01->s03 s02->s03 s03->s04 s04->s03 s03->s05 s07->s03 s03->s08 s08->s03
## [10] s03->s10 s10->s03 s03->s11 s03->s12
Set colors to plot the selected edges.
ecol <- rep("gray80", ecount(net))
ecol[inc.edges] <- "orange"
vcol <- rep("grey40", vcount(net))
vcol[V(net)$media=="Wall Street Journal"] <- "gold"
plot(net,
vertex.color = vcol,
edge.color = ecol)
Highlight immediate neighbors of a vertex using neighbors() to find all nodes one step out from the focal actor. To find the neighbors for multiple nodes, use adjacent_vertices(). To find node neighborhoods going more than one step out, use function ego() with parameter order set to the number of steps out to go from the focal node(s).
(neigh.nodes <- neighbors(net, V(net)[media=="Wall Street Journal"], mode="out"))
## + 7/17 vertices, named, from 0aa98db:
## [1] s01 s04 s05 s08 s10 s11 s12
vcol[neigh.nodes] <- "#ff9d00"
plot(net, vertex.color=vcol)
Highlight a group of nodes by marking them (recall network communities).
par(mfrow=c(1,2))
plot(net,
mark.groups = c(1,4,5,8),
mark.col = "#C5E5E7",
mark.border = NA)
# Mark multiple groups:
plot(net,
mark.groups = list(c(1,4,5,8), c(15:17)),
mark.col = c("#C5E5E7","#ECD89A"),
mark.border = NA)
Other Views of Networks besides Hairballs
Heatmap of network matrix
netm <- as_adjacency_matrix(net, attr="weight", sparse=F)
colnames(netm) <- V(net)$media
rownames(netm) <- V(net)$media
palf <- colorRampPalette(c("gold", "dark orange"))
heatmap(netm[,17:1], Rowv = NA, Colv = NA, col = palf(100),
scale="none", margins=c(10,10) )
Degree distribution for network.
deg.dist <- degree_distribution(net, cumulative=T, mode="all")
plot( x=0:max(deg), y=1-deg.dist, pch=19, cex=1.2, col="orange",
xlab="Degree", ylab="Cumulative Frequency")
2. igraph Visualization with Dataset 2 (Two-Mode Networks)
igraph with Dataset 2
nodes2 <- read_csv(file.path(data_dir,
"Dataset2-Media-User-Example-NODES.csv"))
## First row is names, so need to use read.csv.
links2 <- read.csv(file.path(data_dir,
"Dataset2-Media-User-Example-EDGES.csv"),
header = TRUE,
row.names = 1)
Create a igraph network object from the two-mode matrix:
net2 <- graph_from_incidence_matrix(links2)
## Error in `biadjacency_impl()`:
## ! 'list' object cannot be coerced to type 'double'
Built-in vertex attribute ‘type’ shows which mode vertices belong to.
table(V(net2)$type)
## Error:
## ! object 'net2' not found
plot(net2,vertex.label=NA)
## Error:
## ! object 'net2' not found
To transform a one-mode network matrix into an igraph object, use graph_from_adjacency_matrix().
Identify Media Outlets and Users
Modify network object visual properties – color, size and shape. Media outlets are squares; users are circles.
# Media outlets are blue squares, audience nodes are orange circles:
V(net2)$color <- c("steel blue", "orange")[V(net2)$type+1]
## Error:
## ! object 'net2' not found
V(net2)$shape <- c("square", "circle")[V(net2)$type+1]
## Error:
## ! object 'net2' not found
# Media outlets will have name labels, audience members will not:
V(net2)$label <- ""
## Error:
## ! object 'net2' not found
V(net2)$label[V(net2)$type==F] <- nodes2$media[V(net2)$type==F]
## Error:
## ! object 'net2' not found
V(net2)$label.cex=.6
## Error:
## ! object 'net2' not found
V(net2)$label.font=2
## Error:
## ! object 'net2' not found
plot(net2, vertex.label.color="white", vertex.size=(2-V(net2)$type)*8)
## Error:
## ! object 'net2' not found
Text only for nodes.
plot(net2,
vertex.shape = "none",
vertex.label = nodes2$media,
vertex.label.color = V(net2)$color,
vertex.label.font = 2,
vertex.label.cex = .6,
edge.color = "gray70",
edge.width = 2)
## Error:
## ! object 'net2' not found
Generate bipartite projections
Calculate co-memberships by multiplying the network matrix by its transpose, or using bipartite.projection().
(net2.bp <- bipartite.projection(net2))
## Error:
## ! object 'net2' not found
par(mfrow=c(1,2))
vtype <- c("media","users")[1 + is.na(nodes2$media.type)]
plot(net2.bp$proj1,
vertex.label.color = "black",
vertex.label.dist = 1,
vertex.label = nodes2$media[vtype == "media"])
## Error:
## ! object 'net2.bp' not found
plot(net2.bp$proj2,
vertex.label.color = "black",
vertex.label.dist = 1,
vertex.label = nodes2$media[vtype == "users"])
## Error:
## ! object 'net2.bp' not found
Now force layout to be the same and drop shapes.
par(mfrow=c(1,2))
lay <- layout_with_fr(net2)
## Error:
## ! object 'net2' not found
plot(net2.bp$proj1,
vertex.shape = "none",
vertex.label.color = "black",
vertex.label.dist = 1,
vertex.label = nodes2$media[vtype == "media"],
layout = lay[vtype == "media",])
## Error:
## ! object 'net2.bp' not found
plot(net2.bp$proj2,
vertex.shape = "none",
vertex.label.color = "red",
vertex.label.dist = 1,
vertex.label = nodes2$media[vtype == "users"],
layout = lay[vtype == "users",])
## Error:
## ! object 'net2.bp' not found