Last updated: 2022-07-10

This tutorial is a joint product of the Statnet Development Team:

Pavel N. Krivitsky (University of New South Wales)
Martina Morris (University of Washington)
Mark S. Handcock (University of California, Los Angeles)
Carter T. Butts (University of California, Irvine)
David R. Hunter (Penn State University)
Steven M. Goodreau (University of Washington)
Chad Klumb (University of Washington)
Skye Bender de-Moll (Oakland, CA)
Michał Bojanowski (Kozminski University, Poland)

The specific network modeling software demonstrated in this tutorial is authored by Pavel Krivitsky (ergm.count, ergm.rank and latentnet).


0.1 The statnet Project

All statnet packages are open-source, written for the R computing environment, and published on CRAN. The source repositories are hosted on GitHub. Our website is statnet.org

  • Need help? For general questions and comments, please email the statnet users group at statnet_help@uw.edu. You’ll need to join the listserv if you’re not already a member. You can do that here: statnet_help listserve.

  • Found a bug in our software? Please let us know by filing an issue in the appropriate package GitHub repository, with a reproducible example.

  • Want to request new functionality? We welcome suggestions – you can make a request by filing an issue on the appropriate package GitHub repository. The chances that this functionality will be developed are substantially improved if the requests are accompanied by some proposed code (we are happy to review pull requests).

  • For all other issues, please email us at contact@statnet.org.


1 Getting the software

If you have not already done so, please make sure that you have a reasonably new version of R, preferably the latest (4.2.1) (R Core Team (2013)). Then, download and install the latest versions of the Statnet (Handcock et al. (2008),Goodreau et al. (2008)) packages, in particular ergm version 4.2.2 (Hunter et al. (2008),Handcock et al. (2013)), ergm.count version 4.1.1 (Krivitsky (2013)), ergm.rank version 4.1.0, latentnet version 2.10.6 (Krivitsky and Handcock (2013)), and their dependencies. You can accomplish this by typing:

install.packages("ergm.count")
install.packages("ergm.rank")
install.packages("latentnet")
update.packages()

and then

library(ergm.count)
library(ergm.rank)
library(latentnet)

2 network and edge attributes

network (Butts (2008),Butts, Handcock, and Hunter (n.d.)) objects have three types of attributes:

  • network attributes – attributes which pertain to or are associated with the whole network; this includes basic features such as network size, directedness, and multiplicity, but can include arbitrary information at the network level (e.g., dyadic attributes relating to both observed and unobserved edges).
  • vertex attributes – attributes which are associated with individual vertices in the network; this can include properties such as vertex labels, group assignments, or other properties of the individual nodes.
  • edge attributes – attributes that are defined for edges in the network; these can include missingness labels, edge values, edge types, or other such metadata.

The number of attributes at each level is unlimited, and they may be of any data type; thus, one could define e.g., a network in which every edge contained a network whose edges contained sill other networks, if one wished. This flexibility allows for network objects to be extended in many ways (as is done e.g. in the networkDynamic package, where edges carry complex timing information). Usually, however, we encounter networks whose attributes are numeric (or occasionally categorical). As we will see, there are a number of shortcuts to make life easier in these cases.

Here, we will be especially interested in valued graphs, where edges carry some sort of meaningful value. These are typically stored as edge attributes. Note that an edge attribute is a property of an edge and not a dyad; as such, it is only defined for edges that exist in the network. Thus, in a matter of speaking, to set an edge value, one first has to create an edge and then set its attribute. (In some cases, one needs to associate a value with every dyad, whether or not an edge is present. Typically, these are encoded as network attributes.)

As with network and vertex attributes, edge attributes that have been set can be listed with list.edge.attributes. Every network has at least one edge attribute: "na", which, if set to TRUE, marks an edge as missing. The ergm package, in particular, is frequently able to account for edgewise missingness, and draws this information from the na attribute.

2.1 Constructing valued networks

There are several ways to create valued networks for use with ergm. Here, we will demonstrate two of the most straightforward approaches.

2.1.1 Sampson’s Monks, pooled

The first dataset that we’ll be using is the (in)famous Sampson’s monks. Dataset samplk in package ergm contains three (binary) networks: samplk1, samplk2, and samplk3, containing the Monks’ top-tree friendship nominations at each of the three survey time points. We are going to construct a valued network that pools these nominations.

Method 1: From a sociomatrix In many cases, a valued sociomatrix is available (or can easily be constructed). In this case, we’ll build one from the Sampson data.

data(samplk)
ls()
## [1] "DROPS"   "ext"     "EXTS"    "ifn"     "samplk1" "samplk2" "samplk3"
as.matrix(samplk1)[1:5, 1:5]
##             John Bosco Gregory Basil Peter Bonaventure
## John Bosco           0       0     1     0           1
## Gregory              1       0     0     0           0
## Basil                1       1     0     0           0
## Peter                0       0     0     0           1
## Bonaventure          0       0     0     1           0
# Create a sociomatrix totaling the nominations.
samplk.tot.m <- as.matrix(samplk1) + as.matrix(samplk2) + as.matrix(samplk3)
samplk.tot.m[1:5, 1:5]
##             John Bosco Gregory Basil Peter Bonaventure
## John Bosco           0       1     2     0           2
## Gregory              3       0     0     0           0
## Basil                3       1     0     0           0
## Peter                0       0     0     0           3
## Bonaventure          1       0     0     3           0

# Create a network where the number of nominations becomes an attribute of an
# edge.
samplk.tot <- as.network(samplk.tot.m, directed = TRUE, matrix.type = "a", ignore.eval = FALSE,
    names.eval = "nominations"  # Important!
)
# Add vertex attributes.  (Note that names were already imported!)
samplk.tot %v% "group" <- samplk1 %v% "group"  # Groups identified by Sampson
samplk.tot %v% "group"
##  [1] "Turks"    "Turks"    "Outcasts" "Loyal"    "Loyal"    "Loyal"   
##  [7] "Turks"    "Waverers" "Loyal"    "Waverers" "Loyal"    "Turks"   
## [13] "Waverers" "Turks"    "Turks"    "Turks"    "Outcasts" "Outcasts"

# We can view the attribute as a sociomatrix.
as.matrix(samplk.tot, attrname = "nominations")[1:5, 1:5]
##             John Bosco Gregory Basil Peter Bonaventure
## John Bosco           0       1     2     0           2
## Gregory              3       0     0     0           0
## Basil                3       1     0     0           0
## Peter                0       0     0     0           3
## Bonaventure          1       0     0     3           0

# Also, note that samplk.tot now has an edge if i nominated j *at least once*.
as.matrix(samplk.tot)[1:5, 1:5]
##             John Bosco Gregory Basil Peter Bonaventure
## John Bosco           0       1     1     0           1
## Gregory              1       0     0     0           0
## Basil                1       1     0     0           0
## Peter                0       0     0     0           1
## Bonaventure          1       0     0     1           0

Method 2: Form an edgelist Sociomatrices are simple to work with, but not very convenient for large, sparse networks. In the latter case, edgelists are often preferred. For our present case, suppose that instead of a sociomatrix we have an edgelist with values:

samplk.tot.el <- as.matrix(samplk.tot, attrname = "nominations", matrix.type = "edgelist")
samplk.tot.el[1:5, ]
##      [,1] [,2] [,3]
## [1,]    2    1    3
## [2,]    3    1    3
## [3,]    5    1    1
## [4,]    6    1    2
## [5,]    7    1    1
# and an initial empty network.
samplk.tot2 <- samplk1  # Copy samplk1
samplk.tot2[, ] <- 0  # Empty it out
samplk.tot2  #We could also have used network.initialize(18)
##  Network attributes:
##   vertices = 18 
##   directed = TRUE 
##   hyper = FALSE 
##   loops = FALSE 
##   multiple = FALSE 
##   bipartite = FALSE 
##   total edges= 0 
##     missing edges= 0 
##     non-missing edges= 0 
## 
##  Vertex attribute names: 
##     cloisterville group vertex.names 
## 
## No edge attributes

samplk.tot2[samplk.tot.el[, 1:2], names.eval = "nominations", add.edges = TRUE] <- samplk.tot.el[,
    3]
as.matrix(samplk.tot2, attrname = "nominations")[1:5, 1:5]
##             John Bosco Gregory Basil Peter Bonaventure
## John Bosco           0       1     2     0           2
## Gregory              3       0     0     0           0
## Basil                3       1     0     0           0
## Peter                0       0     0     0           3
## Bonaventure          1       0     0     3           0

In general, the construction net[i,j, names.eval="attrname", add.edges=TRUE] <- value can be used to modify individual edge values for attribute "attrname". This way, we can also add more than one edge attribute to a network. Note that network objects can support an almost unlimited number of vertex, edge, or network attributes, and that these attributes can contain any data type. (Not all data types are compatible with all interface methods; see ?network and related documentation for more information.)

2.1.2 Zachary’s Karate club

The other dataset we’ll be using is almost as (in)famous Zachary’s Karate Club dataset. We will be employing here a collapsed multiplex network that counts the number of social contexts in which each pair of individuals associated with the Karate Club in question interacted. A total of 8 contexts were considered, but as the contexts themselves were determined by the network process, this limit itself can be argued to be endogenous.

Over the course of the study, the club split into two factions, one led by the instructor (“Mr. Hi”) and the other led by the Club President (“John A.”). Zachary also recorded the faction alignment of every regular attendee in the club. This dataset is included in the ergm.count package, as zach.

2.2 Visualizing a valued network

The network’s plot method for networks can be used to plot a sociogram of a network. When plotting a valued network, we it is often useful to color the ties depending on their value. Function gray can be used to generate a gradient of colors, with gray(0) generating black and gray(1) generating white. This can then be passed to the edge.col argument of plot.network.

Sampson’s Monks For the monks, let’s pass value data using a matrix.

par(mar = rep(0, 4))
samplk.ecol <- matrix(gray(1 - (as.matrix(samplk.tot, attrname = "nominations")/3)),
    nrow = network.size(samplk.tot))
plot(samplk.tot, edge.col = samplk.ecol, usecurve = TRUE, edge.curve = 1e-04, displaylabels = TRUE,
    vertex.col = as.factor(samplk.tot %v% "group"))