vignettes/articles/making-network-data.Rmd
making-network-data.RmdStatic preview:
This is a static, read-only preview of the “Making Network Data”
tutorial. To keep it a preview, the code output and exercise solutions
are not shown here, and the quizzes are replaced with notes like this
one. Install the package and run run_tute() at the R
console to work through the tutorial interactively — running the code,
seeing the results, and getting hints, solutions, and quizzes.

Most network analysts want to use network analysis to better understand empirical networks, but before turning to ready-made datasets, it helps to see what a network actually is, stripped down to its simplest form. This tutorial starts by building a tiny network entirely by hand, so that nodes and ties stop being abstract jargon and become something concrete.
From there, manynet offers various functions for finding, importing, and coercing networks into formats you can use. This tutorial covers:
It also covers how to describe that data once you have it,
and how to coerce it between the many different classes
(matrices, edgelists, igraph, network,
tidygraph objects) used for network analysis in R.
One might additionally create structured networks algorithmically (lattices, rings, stars, and the like) or stochastically generate random ones, and manynet includes functions for this too, but we cover these in the later tutorials on topology and diffusion (found in the netrics and migraph packages respectively). A companion tutorial, “Manipulating Data”, covers how to modify, reformat, and transform network data once you have collected it. Tutorials on visualising networks can be found in the autograph package.
Let’s start from scratch. Stripped to its essentials, a network is simply a set of nodes (also called vertices or actors) and the ties (also called edges or links) connecting them. There may be other features, such as nodal or tie attributes, but the without these two elements, there is no network.
New to network vocabulary?: Throughout this tutorial, key terms are italicised: hover over them for a definition, and a full glossary of the terms used appears at the end of the tutorial.
The quickest way to get a feel for how a network is constructed is to
type one out yourself. manynet’s
create_explicit() lets you do just that, naming nodes and
ties directly in a compact formula. Ties are written with a dash,
-, between two node names, and separate ties are divided by
commas. So create_explicit(A-B, B-C, C-A) makes a triangle
between A, B, and C. You can add an unconnected node (an
isolate ) just by naming it after a comma,
e.g. create_explicit(A-B, C). Run the following to
build a small friendship network by hand.
create_explicit(Amir-Bao, Bao-Chen, Chen-Amir, Dara)Notice the printout describes a labelled, undirected network of 4 nodes and 3 ties: the three names joined by dashes, plus Dara as an isolate .
To make a
directed network instead, add a + on the
arrowhead end of a tie: A-+B points from A to B,
A+-B points from B to A, and A+-+B makes a
mutual
(
reciprocated ) pair. Try defining a network where
Amir nominates the others.
The printout should now say directed, and report arcs rather than ties (an arc is a directed tie).
Going further:
manynet can even help you collect an ego network
through an interactive interview, with collect_ego(). It
asks you, at the R console, for ego’s name, the relationship, and each
of ego’s contacts (alters) in turn, then builds the network for you.
Because it works by asking questions and waiting for your (or your
respondents’) typed replies, it cannot run inside this tutorial window —
but try obj <- collect_ego() in your own R console
sometime to see it work!
Typing out ties like this is manageable for very small networks, but imagine doing it for networks of 100 nodes or more… it would be tedious, slow, and easy to get wrong. Fortunately, there are lots of data already collected and available for use in R packages, or that can be imported from files. Let’s see what manynet already has on offer.
In brief:
create_explicit() builds a small network from a formula of
named ties — - for an (undirected) tie,
-+/+-/+-+ for directed arcs, and
a lone name for an isolate, separated by commas. For larger
hand-collected data you would usually import an edgelist (see below),
and collect_ego() can gather an ego network interactively
at the console.
On this page: Finding the data · Calling the data · Free play
As many R packages do, manynet includes a number of datasets used for teaching and testing the functions contained in the package. These are sometimes classical network datasets, such as the Southern Women dataset or Zachary’s Karateka dataset, and sometimes new data with neat themes, features, or attributes that make them exemplar teaching or testing data.
To see what data is in the package, you can explore the documentation available on the website (see here) or use a function in R to list the data available in the package.
One function, available in base R (with no added packages), is
data(package = "manynet"). Type this in to the box
below to see what datasets are available in the package. There
are buttons to start over, receive any hints/solutions available, as
well as to run the code you have entered to discover its effects. Try it
out now!
On the left of the output are the names of the objects (starting with
fict_* for fictional networks, irps_* for
networks from international relations and political science, and
ison_* for classical networks in the teaching literature).
On the right is a brief description of the networks and their canonical
source. Do you recognise any of the datasets?
manynet also includes its own way of identifying
network data in a package. table_data() returns a table of
the network datasets in a package, along with information about the
number of nodes, ties, and various other features. Run the
following code to see this in action.
Let’s say that we are only interested in
two-mode network data, that is, networks where the nodes
belong to two different sets (such as people and the events they attend)
and ties connect only nodes from different sets. How can we filter this
table so that only those networks that are two-mode are retained?
table_data() returns a regular tibble (data frame), so we
can use dplyr’s filter() on any of its
logical columns.
Beginner note:
The |> symbol below is called a ‘pipe’. It passes the
result of the function on its left on to the function on its right, so
table_data() |> dplyr::filter(twomode) means “take the
table of datasets, then keep only the rows where
twomode is TRUE”. Piping or ‘chaining’ functions like this
is very common in modern R, and we will use it throughout these
tutorials.
table_data() |> dplyr::filter(twomode)Try it yourself:
This section includes an interactive quiz in the live tutorial — run
run_tute() at the R console to try it.
Ok, so we can see that there are a number of very interesting datasets available in this package. How do we access and use this data?
The easiest way to call the data is just to make sure that the
package is loaded using the command library(manynet), and
then use the selected dataset as named above. 1 Try calling
ison_adolescents by first loading the
manynet ‘library’ and then just typing
ison_adolescents to see what happens.
If it worked, you should see a printout describing an undirected network of 8 nodes and 10 ties, followed by tables of its nodes and ties. We will learn how to read this printout in the next section.
Choose your own data: The worked examples below use small, clean, classical datasets so that the output is easy to read. But wherever you see a “Your turn” or “Free play” box, you are encouraged to swap in a network that interests you. The datasets in manynet come in three flavours, and you can think of them as a rough difficulty ladder:
ison_*) — small, tidy,
textbook networks. Easiest to read.
fict_*) — mid-sized networks
from film and TV (Lord of the Rings, Grey’s Anatomy, Star Wars, …).
A step up.
irps_*) — larger networks
from international relations and political science. Closest to real
analysis.
Because every manynet function works on any network of
any class or type, you can substitute any of these datasets into the
exercises and everything will still work — only the numbers will change.
To browse what is on offer, and even filter by flavour, use
table_data() to list the two-mode fiction networks, for
example:
table_data() |> dplyr::filter(twomode) |> dplyr::filter(grepl("fict", dataset))See whether you can call up other datasets now too.
You won’t need to load the manynet package again (it’ll
stay loaded), but identify a network that interests you and then
call/print it. Not sure which to pick? Here is one suggestion per
flavour — choose whichever appeals, or find your own with
table_data():
| Classic (small, easy) | Fiction (moderate) | Real-world (larger) |
|---|---|---|
ison_adolescents (8 nodes) |
fict_lotr (36 nodes) |
irps_usgeo (50 nodes) |
ison_brandes (11 nodes) |
fict_greys (53 nodes) |
irps_books (105 nodes) |
Whichever you choose, the printout has the same shape you saw above:
a one-line summary, then a table of nodes and a table of ties. Try
running net_nodes() and net_ties() on your
chosen network too (we meet these functions properly in the next
section).
In brief:
data(package = "manynet") and table_data()
list the datasets bundled with a package, and table_data()
additionally reports their main features in a filterable table. Once the
package is loaded with library(manynet), any dataset can be
used just by typing its name.
On this page: Reading prints · Grabbing details · Free play
All of the network data available in manynet (and
migraph) are in a special tbl_graph format,
from the tidygraph package, that makes it compatible,
flexible, and transparent. When you call one of these data objects, some
information about the type of network it is, how many nodes and ties it
has, and the first few examples of nodes and ties is given. Let’s see
whether we can make sense of the main features of this network.
Run the following line and then answer the questions
below.
ison_adolescentsThe first line of the printout summarises the network’s type and dimensions. The ‘Nodes’ table then lists the nodes and any nodal attributes, and the ‘Ties’ table lists which node each tie goes ‘from’ and ‘to’. Only the first few rows of each table are shown. Look for the ‘… with x more’ note beneath each table to get the full counts.
Try it yourself:
This section includes an interactive quiz in the live tutorial — run
run_tute() at the R console to try it.
You can now describe the main dimensions and type of network. In the visualisation tutorial (in the autograph package), we will see how we can describe such networks visually.
We can ask other questions of this data too. manynet uses a simple function naming convention so that you always know what you can expect a function to return:
net_*() functions usually return one value for the
network or graph, whether that be a string like Evelyn or
some number like 3 or -0.003
node_*() functions always return a vector of values as
long as the number of nodes or vertices in the network (of any
mode)tie_*() functions always return a vector of values as
long as the number of ties or edges in the network (of any sign or
type)mode_*() functions always return a vector of values as
long as the number of modes or nodesets in the networklayer_*() functions always return a vector of values as
long as the number of layers or types of ties in a networkTo find out how many nodes are in the network, use
net_nodes(). To find out how many nodes are in each mode,
use mode_nodes(). To find out the names of those nodes, use
node_labels(). Use such functions to find
out:
ison_southern_women
networkTo check your results: this network has 32 nodes in total, 18 in the first mode and 14 in the second, and 89 ties between them.
There are a bunch of logical checks for many common properties or
features of networks. For example, one can check whether a network
is_twomode(), is_directed(), or
is_labelled(). Remember, all is_*() functions
work on any compatible class.
Your turn: ison_southern_women is a
two-mode (or bipartite) network, one of the more interesting
network types. Run the same battery of functions on another two-mode
network to compare. Pick whichever flavour appeals:
| Classic | Real-world |
|---|---|
ison_southern_women (women × events) |
irps_revere (Paul Revere: people × organisations) |
(The fiction two-mode networks such as fict_actually are
also multiplex, which adds a complication we set aside for
now.) Whatever you choose, is_twomode() should return
TRUE, and mode_nodes() should return two
numbers rather than one.
In brief: When
printed, networks report their type, dimensions, and tables of nodes and
ties. net_*() functions return one value about the whole
network, node_*() functions return one value per node,
tie_*() functions return one value per tie,
mode_*() functions return one value per mode,
layer_*() functions return one value per layer, and
is_*() functions return TRUE/FALSE checks of network
properties.
On this page: Network class objects · Class coercion
We can describe and work with networks from other R packages too, not
just those in the tbl_graph format. For example, another
commonly used package in network analysis is network,
which includes a few example datasets of its own. Can you
remember how to find out which data are available in this package? Find
out, and call the last dataset in the list.
This data uses quite a different class to what we encountered above.
It prints out the full adjacency matrix of the Florentine network, but
as a network-class object (i.e. from the
network package). This is no problem for
manynet (or migraph), since every included
function works the same on any of the compatible classes, but in case
you would like to work with a network in a particular class, or it needs
to be in a particular format for further work (e.g. for use with
ergm), then manynet has you covered for
that too.
Coercing networks between different classes of objects uses the
as_*() functions. ‘Coercion’ just means converting an
object from one class to another, here for example from a
network-class object to an igraph or
tbl_graph object. These functions will do their best to
coerce data from the current class of the object to the class named in
the function. Some classes have ‘slots’ or recognition for some kinds of
information that others don’t. For example, coercing a
tbl_graph into an edgelist will sacrifice all the
information about nodal attributes. Still, we aim for these functions to
be as lossless as possible and welcome feedback that highlights how
these translations can be improved. Let’s see whether we can
coerce our ‘flo’ network into a tbl_graph (‘tidygraph’)
class object. Print the object before and after coercing it, so
that you can see what has changed.
Compare the two printouts: the same network that printed as a large adjacency matrix of 0s and 1s now prints as a compact description with separate tables of nodes and ties. No information about the network itself has been lost — only the ‘container’ has changed.
Try it yourself:
This section includes an interactive quiz in the live tutorial — run
run_tute() at the R console to try it.
Other packages that include network data include David Schoch’s
descriptively named {networkdata}
package. The data in this package are igraph-class
objects. Can you coerce one of the datasets in this package into a
tidygraph format? Into a network format? Into a matrix? Into an
edgelist?
In brief: The
as_*() functions (as_igraph(),
as_tidygraph(), as_network(),
as_matrix(), as_edgelist()) coerce network
data from any compatible class into the named class, as losslessly as
possible. Since all manynet functions work on all
compatible classes, you rarely need to coerce, but it is there
when another package expects a specific format.
On this page: Finding data · Importing edgelists · Exporting edgelists · Importing other formats
Researchers will regularly find themselves needing to import and work with network data from outside of R. There are a great number of networks datasets and data resources available online. Some of these are specifically social networks datasets, while others are more general datasets that can (also) be analysed as networks. Some strong collections of networks datasets can be found at the following locations:
These resources contain data in a range of different formats though. Some are specifically made to work with certain software, others rely on open standards, and still others keep data in a very standard edgelist (and perhaps nodelist) format in .csv files or similar. Fortunately, manynet has functions to help with importing data from such formats too.
One format most users are long familiar with is Excel. In Excel,
users are typically collecting network data as edgelists, nodelists, or
both. Recall that an
edgelist tabulates the senders/from and receivers/to of
each tie in the first two columns and any other edge- or tie-related
attributes as additional columns. There may optionally also be a
nodelist that tabulates the nodes in the network along
with any nodal attributes. Edgelists are typically the main object to be
imported, and we can import them from an Excel file or a
.csv file.2 For the sake of this exercise, we’ll import
some data, adols.csv, that I’ve pre-saved within the
package in the data/ folder of this tutorial. Try
the following code chunk.
adolties <- read_edgelist("data/adols.csv")
adolties
flonodes <- read_nodelist("data/flonode.csv")
flonodesIf you do not specify a particular file name, a helpful popup will
open that assists you with locating and importing a file from your
operating system. Importing a nodelist of nodal attributes operates very
similarly, with read_nodelist().
In some cases, users will be faced with having to collect data
themselves, or wish to first manipulate the data in Excel before
importing it, but may be uncertain about the expected format of an
edgelist. Here it may be useful to try exporting one of the built-in
datasets in manynet to see how complete network data
looks. If this is potentially complex, calling
write_edgelist() without any arguments will export a test
file with a barebones structure that you can overwrite with your own
data. Try exporting the ties and the nodes of the
fict_lotr network to Excel files in R’s temporary
directory. (Feel free to substitute a network of your own
choosing — try one with richer nodal attributes, such as
ison_lawfirm or irps_usgeo, to see more
columns in the exported nodelist.)
To check your results: no output means it worked! The two files are
written to a temporary folder (run tempdir() to see where
that is on your computer); in your own projects you would give a path in
your own working directory instead.
Since network data can be complex, edgelists (and nodelists) may not be sufficient to structure all the information necessary to represent the network. For this reason, a variety of other external formats have been proposed and used. As such, you may find network data of interest that is in another format. Here are some examples:
read_pajek() and write_pajek() for
importing and exporting .net or .paj filesread_ucinet() and write_ucinet() for
importing and exporting .##h files (.##d files are automatically
imported alongside them)read_graphml() and write_graphml() for
importing and exporting .graphml filesread_dynetml() for importing .dynetml filesFor more information on any of these functions, you can ask for help
by typing ?read_pajek in the console. Whereas
read_edgelist() and read_nodelist() will
import into a tibble/data frame class, read_pajek() and
read_ucinet() will import the network into a tidygraph
format (see above). Of course, any network data that is imported can be
easily coerced into any other compatible class. Let’s say we want to
import the adolescents edgelist back in, but we want it in an igraph
format. There are three ways you might do this — run the code
and compare the three results.
# 1. Separate steps
adols <- read_edgelist("data/adols.csv")
adolsigraph1 <- as_igraph(adols)
adolsigraph1
# 2. Nested steps
adolsigraph2 <- as_igraph(read_edgelist("data/adols.csv"))
adolsigraph2
# 3. Chained steps
adolsigraph3 <- read_edgelist("data/adols.csv") %>% as_igraph()
adolsigraph3All three ways produce exactly the same result; which one you use in
your own work is a matter of taste and readability. How does the result
compare to the original ison_adolescents?
Try it yourself:
This section includes an interactive quiz in the live tutorial — run
run_tute() at the R console to try it.
In brief:
read_*()/write_*() functions import and export
edgelists and nodelists (from/to .csv or Excel) as well as Pajek,
UCINET, GraphML, and DyNetML formats. Whatever the import class, the
as_*() coercion functions will get the data into whatever
class you need next.
Well done — you have completed the tutorial on making network data! Along the way, you have learned to use these functions:
| Function | What it does |
|---|---|
table_data() |
lists a package’s network datasets and their features |
create_explicit() |
build a small network by hand from a formula of named ties |
collect_ego() |
gather an ego network interactively at the console |
net_nodes(), net_ties(),
mode_nodes()
|
count a network’s nodes, ties, and nodes per mode |
node_labels(), net_node_attributes()
|
list node names and nodal attributes |
is_twomode(), is_directed(),
is_labelled(), … |
TRUE/FALSE checks of network properties |
as_igraph(), as_tidygraph(),
as_network(), as_matrix(),
as_edgelist()
|
coerce networks between classes |
read_edgelist(), read_nodelist(),
write_edgelist(), write_nodelist()
|
import/export edgelists and nodelists |
read_pajek(), read_ucinet(),
read_graphml(), read_dynetml(), … |
import/export other network formats |
When you are ready, continue with the companion tutorial “Manipulating Data”, which covers how to modify, reformat, and transform the networks you now know how to collect.
Here are some of the terms that we have covered in this tutorial:
Alternatively, the data can be called directly out of
the package like this:
example_name <- manynet::ison_adolescents, but since we
think you will probably want all of the other functions available in
manynet at your disposal, you may as well just load the
package entirely.↩︎
Note that if you import from a .csv file, please specify
whether the separation value should be commas
(sv = "comma") or semi-colons
(sv = "semi-colon"). The function expects comma separated
values by default.↩︎