3 Creating network objects in R

Now that we have our edge lists and node lists, we want to create our first network object. We are going to start by making a one-mode network.

There are two popular network packages (and consequently, network object types) in R. igraph generates a list object of class “igraph” and network generates an list object of class “network”. We are going to be working primarily with network objects, but I’d like to start by making both because you are likely to run into both of them along the way, and both are compatible with the ggraph package that we will be using.

So, let’s read in our two network packages.

library(igraph)
library(network)

3.1 igraph

First, let’s make an igraph object. igraph generally seems to have more documentation/Stack Overflow resources than other network packages, so it might be tempting to start here. We can load in the igraph package and use the graph_from_data_frame() function to create our graph. Note that there are dozens of graph_from... functions. Many of them sound (and are) intuitive. When dealing with edge and node lists, I prefer graph_from_data_frame() over graph_from_edgelist() because the former allows us to include the node list as an argument, whereas the latter does not.

For our one-mode networks we want to use the edges_1mode and nodes_1mode objects.

g1 <- graph_from_data_frame(edges_1mode, vertices = nodes_1mode, directed = F)

We can take a look at a summary of the igraph object below. We see the number of nodes (139) and edges (475) summarized at top and our list of node attributes (followed by (v/[datatype])) and edges attributes (followed by (e/[datatype])).

summary(g1)
## IGRAPH a659ffa UN-- 139 475 -- 
## + attr: name (v/c), url (v/c), mode (v/n), org_type (v/c), Y1995_2009
## | (e/l), Y2010_2024 (e/l), before_1980 (e/l), Y1980_1994 (e/l)

The syntax for manipulating igraphs is demonstrated below. We use the V() and E() functions to call on either vertices or edges of our igraph, respective. hen can use the $ symbol to subset from those elements of the network.

# Subset the vertex attribute: the names of the vertices
head(V(g1)$name)
## [1] "Agricultural Coalitions: Landowners membership fees"
## [2] "Anchor QEA"                                         
## [3] "Audubon Canyon Ranch"                               
## [4] "Bachand and Associates"                             
## [5] "BTS"                                                
## [6] "CalFish"
# Subset the edge attribute: a logical value for whether a collaboration
# happened between 1995-2009
head(E(g1)$Y1995_2009)
## [1]  TRUE FALSE FALSE FALSE FALSE FALSE

3.2 network

Personally, I find that the igraph package has more limited statistical functionality than network package (and its associated statnet suite of packes), so I prefer network objects. We initialize a network object with the network function (network really needs to get more creative in its naming approaches). Just like with igraph, for our one-mode network we will use our projected edges and mode one nodes.

Note: You’ll often see folks converting igraphs to network objects using the intergraph package.

library(intergraph)
net1_convert <- asNetwork(g1)

This conversion function can be very useful, but be sure to double check your work. Right now, with a relatively simple network (undirected and unweighted) the conversion works fine, but in other cases the default settings may require some attention. Because of this, personally I like to generate network objects directly from my edge and node lists.

Within the network package we can use the network function and read in our edge lists (as the x argument), vertices, and specify features of the network such as whether or not it is directed or bipartite.

net1 <- network(x = edges_1mode, 
               vertices = nodes_1mode,
               bipartite = F,  
               directed = F)

When we print out a network object we get a different-looking summary, but it generally carries the same information:

net1
##  Network attributes:
##   vertices = 139 
##   directed = FALSE 
##   hyper = FALSE 
##   loops = FALSE 
##   multiple = FALSE 
##   bipartite = FALSE 
##   total edges= 475 
##     missing edges= 0 
##     non-missing edges= 475 
## 
##  Vertex attribute names: 
##     mode name org_type url vertex.names 
## 
##  Edge attribute names: 
##     before_1980 Y1980_1994 Y1995_2009 Y2010_2024

The syntax for manipulating network objects is demonstrated below. First, we can use network’s series of get. functions to index components of the network. For node attributes, you can use get.vertex.attribute:

head(get.vertex.attribute(net1, 'name'))
## [1] "Agricultural Coalitions: Landowners membership fees"
## [2] "Anchor QEA"                                         
## [3] "Audubon Canyon Ranch"                               
## [4] "Bachand and Associates"                             
## [5] "BTS"                                                
## [6] "CalFish"

For edge attributes, you can use get.edge.attribute:

head(get.edge.attribute(net1, 'before_1980'))
## [1] FALSE FALSE FALSE FALSE FALSE FALSE

You can also use special operators, %v% and %e%, call on either vertices or edges of our network and then name the attribute in quotation marks

head(net1 %v% 'name')
## [1] "Agricultural Coalitions: Landowners membership fees"
## [2] "Anchor QEA"                                         
## [3] "Audubon Canyon Ranch"                               
## [4] "Bachand and Associates"                             
## [5] "BTS"                                                
## [6] "CalFish"
head(net1 %e% 'Y1995_2009')
## [1]  TRUE FALSE FALSE FALSE FALSE FALSE

3.2.1 Preparing to work with network

For the remainder of this tutorial are going to stick to working with the network package, even though the functions we will use for visualization are compatible with both objects. I propose using network objects because they are compatible with more advanced statistical analysis provided through the statnet suite of packages.

Because we will be using exclusively network objects, we want to detach igraph before we continue further. This is because there are several commonly-used network functions in both igraph and sna that mask one another. For instance:

sna::degree()
igraph::degree()

The igraph degree function will work only on igraph objects and sna degree function will work only on network objects. To avoid confusion and masking in R, we are going to detach the igraph package and work only with network objects and compatible packages like sna.

detach("package:igraph", unload = TRUE)