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:

  1. specify them in plot() function
  2. add them to the igraph object

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)

plot of chunk unnamed-chunk-17

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)

plot of chunk unnamed-chunk-18

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) 

plot of chunk unnamed-chunk-19

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")

plot of chunk unnamed-chunk-20

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) 

plot of chunk unnamed-chunk-21

Override attributes explicitly in the plot

plot(net, edge.color="orange", vertex.color="gray50") 

plot of chunk unnamed-chunk-22

Legend

Add legend explaining color meaning.

  • x,y: legend coordinates
  • pch: element symbol
  • pt.bg: background color
  • col: border color,
  • pt.cex: symbol size
  • bty: type of box around legend
  • ncol: 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)

plot of chunk unnamed-chunk-23

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)

plot of chunk unnamed-chunk-24

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 of chunk unnamed-chunk-25

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")

plot of chunk unnamed-chunk-26

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")

plot of chunk unnamed-chunk-27

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])

plot of chunk unnamed-chunk-28

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")

plot of chunk unnamed-chunk-30

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)

plot of chunk unnamed-chunk-33

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)

plot of chunk unnamed-chunk-36

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)

plot of chunk unnamed-chunk-38

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)

plot of chunk unnamed-chunk-39

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) )

plot of chunk unnamed-chunk-40

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")

plot of chunk unnamed-chunk-41


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

This site uses Just the Docs, a documentation theme for Jekyll.